<template>
  <div v-if="visible" class="yard-detail-overlay" @click="handleOverlayClick">
    <div class="yard-detail-modal" @click.stop>
      <!-- 模态框头部 -->
      <div class="modal-header">
        <div class="header-content">
          <h2 class="modal-title">{{ yardData?.yardId || '未知' }} 堆场俯瞰图</h2>
        </div>
        <button class="close-btn" @click="closeModal" title="关闭">
          <span class="close-icon">×</span>
        </button>
      </div>

      <!-- 模态框内容 -->
      <div class="modal-content">

        <!-- 简单加载状态遮罩 -->
        <div
            v-if="showLoadingState"
            class="loading-overlay"
            :class="{ 'fade-out': loadingState.isFullyLoaded }"
        >
          <div class="yard-loading">
            <div class="loading-title">{{ loadingState.loadingMessage }}</div>
            <div class="loading-progress">
              <div class="progress-bar">
                <div
                    class="progress-fill"
                    :style="{ width: `${loadingState.renderProgress}%` }"
                ></div>
              </div>
              <div class="progress-text">
                {{ loadingState.renderProgress }}%
              </div>
            </div>
          </div>
        </div>

        <div class="yard-view" :data-block="yardData?.yardId">
          <!-- 操作模式切换Tab - 放在堆场图左上角 -->
          <div class="operation-tabs">
            <div class="tab-list">
              <button 
                class="tab-item" 
                :class="{ 'active': currentMode === 'view' }"
                @click="switchMode('view')"
              >
                <span class="tab-text">查看</span>
              </button>
              <button 
                class="tab-item" 
                :class="{ 'active': currentMode === 'plan' }"
                @click="switchMode('plan')"
              >
                <span class="tab-text">计划</span>
              </button>
              <button 
                class="tab-item" 
                :class="{ 'active': currentMode === 'lock' }"
                @click="switchMode('lock')"
              >
                <span class="tab-text">加锁</span>
              </button>
            </div>
          </div>
          
          <!--堆场图-->
          <div v-if="yardData" class="yard-grid" :class="getGridClass()" ref="yardGrid" @mousedown="startSelection" @mouseup="endSelection">
            <!-- 排号 -->
            <div class="yard-row" v-for="row in (yardData?.rowCount || 0)" :key="row">
              <div
                  class="bay-number"
                  :class="{ 'empty': !shouldShowRow(row) }"
                  :style="rowHeaderStyle"
              >
                {{ shouldShowRow(row) ? formatRowNumber(row) : '' }}
              </div>

              <!-- 单个位置 -->
              <div
                  v-for="bay in (yardData?.bayCount || 0)"
                  :key="bay"
                  class="grid-cell"
                  :class="getCellClass(bay, row)"
                  :style="cellStyle"
                  :data-col="getDisplayBay(bay)"
                  :data-row="getDisplayRow(row)"
                  @mouseenter="onCellMouseEnter"
                  @dblclick="showBayViewer(bay, row)"
              ></div>
            </div>

            <!-- 贝号 -->
            <div class="column-headers">
              <!--堆场号-->
              <div class="bay-header" :style="blockStyle">{{ yardData.yardId }}</div>
              <div
                  v-for="bay in (yardData?.bayCount || 0)"
                  :key="bay"
                  class="column-header"
                  :class="{ 
                    'empty': !shouldShowBay(bay),
                    'bay-hovered': currentMode === 'view' && hoveredBay === bay
                  }"
                  :style="bayHeaderStyle"
                  @mouseenter="onBayHeaderHover(bay)"
                  @mouseleave="onBayHeaderLeave"
                  @dblclick="showBayViewer(bay)"
              >
                {{ shouldShowBay(bay) ? formatBayNumber(bay) : '' }}
              </div>
            </div>

            <!-- 集装箱覆盖层 - 使用渐进式渲染 -->
            <div
                v-for="container in displayContainers"
                :key="`${container.bay}-${container.row}-${container.tier || 1}`"
                class="container-overlay"
                :class="getContainerOverlayClass(container)"
                :style="getContainerOverlayStyle(container)"
                @mouseenter="onContainerOverlayHover(container)"
                @mouseleave="onContainerOverlayLeave(container)"
                @click="onContainerOverlayClick(container)"
            >
              <div class="container-3d-wrapper">
                <div class="stack">
                  <!-- 根据集装箱层级数量显示相应数量的长方体 -->
                  <div
                      v-for="tier in getContainerTierCount(container)"
                      :key="tier"
                      class="cuboid"
                      :class="`cuboid-${tier}`"
                      :style="[getCuboidConfig(container), { transform: getStackPosition(tier, container) }]"
                      :title="getContainerTooltip(getContainerAtTier(container.bay, container.row, tier))"
                  >
                    <div class="face front" :style="getFaceStyles(container).front">{{ tier }}</div>
                    <div class="face back" :style="getFaceStyles(container).back"></div>
                    <div class="face right" :style="getFaceStyles(container).right"></div>
                    <div class="face left" :style="getFaceStyles(container).left"></div>
                    <div class="face top" :style="getFaceStyles(container).top"></div>
                    <div class="face bottom" :style="getFaceStyles(container).bottom"></div>
                  </div>
                </div>
              </div>
            </div>

            <!-- 计划区域覆盖层 -->
            <div
                v-for="plan in yardPlans"
                :key="plan.planId"
                class="plan-overlay"
                :class="getPlanOverlayClass(plan)"
                :style="getPlanOverlayStyle(plan)"
                :title="getPlanTooltip(plan)"
                @mouseenter="onPlanOverlayHover(plan)"
                @mouseleave="onPlanOverlayLeave(plan)"
                @click="onPlanOverlayClick(plan)"
            >
              <!-- 拖拽控制点 -->
              <div
                  v-if="selectedPlanId === plan.planId"
                  class="plan-resize-handles"
                  :class="{ 'resizing': planResizeState.isResizing && planResizeState.resizingPlanId === plan.planId }"
              >
                <!-- 上边控制点 -->
                <div
                    class="resize-handle resize-handle-n"
                    @mousedown="startResize($event, plan, 'n')"
                    title="拖拽调整上边"
                ></div>
                <!-- 右边控制点 -->
                <div
                    class="resize-handle resize-handle-e"
                    @mousedown="startResize($event, plan, 'e')"
                    title="拖拽调整右边"
                ></div>
                <!-- 下边控制点 -->
                <div
                    class="resize-handle resize-handle-s"
                    @mousedown="startResize($event, plan, 's')"
                    title="拖拽调整下边"
                ></div>
                <!-- 左边控制点 -->
                <div
                    class="resize-handle resize-handle-w"
                    @mousedown="startResize($event, plan, 'w')"
                    title="拖拽调整左边"
                ></div>
              </div>
            </div>

            <!-- 加锁区域覆盖层 -->
            <div
                v-for="lock in yardLocks"
                :key="lock.lockId"
                class="lock-overlay"
                :class="getLockOverlayClass(lock)"
                :style="getLockOverlayStyle(lock)"
                :title="getLockTooltip(lock)"
                @mouseenter="onLockOverlayHover(lock)"
                @mouseleave="onLockOverlayLeave(lock)"
                @click="onLockOverlayClick(lock)"
            >
              <!-- 锁图标 -->
              <div class="lock-icon">🔒</div>
            </div>
          </div>
        </div>
      </div>

      <!-- 计划信息面板 -->
      <div v-if="selectionInfo && currentMode === 'plan'" class="selection-info-panel">
        <h4>选中区域信息</h4>
        <div class="info-item">
          <span class="label">堆场号:</span>
          <span class="value">{{ selectionInfo.yardId }}</span>
        </div>
        <div class="info-item">
          <span class="label">开始贝:</span>
          <span class="value">{{ selectionInfo.startBay }}</span>
        </div>
        <div class="info-item">
          <span class="label">结束贝:</span>
          <span class="value">{{ selectionInfo.endBay }}</span>
        </div>
        <div class="info-item">
          <span class="label">开始排:</span>
          <span class="value">{{ selectionInfo.startRow }}</span>
        </div>
        <div class="info-item">
          <span class="label">结束排:</span>
          <span class="value">{{ selectionInfo.endRow }}</span>
        </div>
        <div class="info-item">
          <span class="label">选中区域:</span>
          <span class="value">{{ selectionInfo.areaDescription }}</span>
        </div>
        <div class="info-item">
          <span class="label">单元格数:</span>
          <span class="value">{{ selectionInfo.selectedCount }}</span>
        </div>

        <!-- 保存计划区域 -->
        <div class="save-plan-section">
          <h5>保存为堆场计划</h5>
          <div class="info-item">
            <span class="label">计划描述:</span>
            <input
                v-model="planDescription"
                type="text"
                class="plan-description-input"
                :placeholder="`${selectionInfo.yardId}堆场${selectionInfo.startBay}-${selectionInfo.endBay}贝${selectionInfo.startRow}-${selectionInfo.endRow}排区域`"
            />
          </div>
          <div class="button-group">
            <button @click="saveCurrentSelectionAsPlan" class="save-plan-btn" :disabled="isSavingPlan">
              {{ isSavingPlan ? '保存中...' : '保存计划' }}
            </button>
            <button @click="clearSelection" class="clear-selection-btn">
              清除计划 ({{ selectionInfo.selectedCount }}个单元格)
            </button>
          </div>
        </div>
      </div>

      <!-- 加锁信息面板 -->
      <div v-if="lockInfo && currentMode === 'lock'" class="lock-info-panel">
        <h4>加锁区域信息</h4>
        <div class="info-item">
          <span class="label">堆场号:</span>
          <span class="value">{{ lockInfo.yardId }}</span>
        </div>
        <div class="info-item">
          <span class="label">开始贝:</span>
          <span class="value">{{ lockInfo.startBay }}</span>
        </div>
        <div class="info-item">
          <span class="label">结束贝:</span>
          <span class="value">{{ lockInfo.endBay }}</span>
        </div>
        <div class="info-item">
          <span class="label">开始排:</span>
          <span class="value">{{ lockInfo.startRow }}</span>
        </div>
        <div class="info-item">
          <span class="label">结束排:</span>
          <span class="value">{{ lockInfo.endRow }}</span>
        </div>
        <div class="info-item">
          <span class="label">加锁区域:</span>
          <span class="value">{{ lockInfo.areaDescription }}</span>
        </div>
        <div class="info-item">
          <span class="label">单元格数:</span>
          <span class="value">{{ lockInfo.lockedCount }}</span>
        </div>

        <!-- 保存加锁区域 -->
        <div class="save-lock-section">
          <h5>保存为加锁区域</h5>
          <div class="info-item">
            <span class="label">加锁描述:</span>
            <input
                v-model="lockDescription"
                type="text"
                class="lock-description-input"
                :placeholder="`${lockInfo.yardId}堆场${lockInfo.startBay}-${lockInfo.endBay}贝${lockInfo.startRow}-${lockInfo.endRow}排区域`"
            />
          </div>
          <div class="button-group">
            <button @click="saveCurrentLockAsArea" class="save-lock-btn" :disabled="isSavingLock">
              {{ isSavingLock ? '保存中...' : '保存加锁' }}
            </button>
            <button @click="clearLockSelection" class="clear-lock-btn">
              清除加锁 ({{ lockInfo.lockedCount }}个单元格)
            </button>
          </div>
        </div>
      </div>

      <!-- 保存成功提示 -->
      <div v-if="showSaveSuccess" class="save-success-toast">
        <div class="toast-content">
          <div class="toast-icon">✓</div>
          <div class="toast-message">{{ saveSuccessMessage }}</div>
          <button @click="hideSaveSuccess" class="toast-close">×</button>
        </div>
      </div>

      <!-- 加锁成功提示 -->
      <div v-if="showLockSuccess" class="lock-success-toast">
        <div class="toast-content">
          <div class="toast-icon">🔒</div>
          <div class="toast-message">{{ lockSuccessMessage }}</div>
          <button @click="hideLockSuccess" class="toast-close">×</button>
        </div>
      </div>

      <!-- 模态框底部 -->
      <div class="modal-footer">
        <button class="btn btn-secondary" @click="closeModal">关闭</button>
        <button class="btn btn-primary" @click="refreshData">刷新数据</button>
        <button v-if="currentMode === 'plan'" class="btn btn-warning" @click="clearSelection">清除计划</button>
        <button v-if="currentMode === 'plan' && selectPlan.selectedCells.size > 0" class="btn btn-success" @click="saveCurrentSelectionAsPlan">{{ isSavingPlan ? '保存中...' : '保存为计划' }}</button>
        <button v-if="currentMode === 'lock'" class="btn btn-warning" @click="clearLockSelection">清除加锁</button>
        <button v-if="currentMode === 'lock' && selectLock.lockedCells.size > 0" class="btn btn-success" @click="saveCurrentLockAsArea">{{ isSavingLock ? '保存中...' : '保存为加锁' }}</button>
      </div>
    </div>
  </div>
</template>

<script>
import dataApiService from "@/services/api.js";

export default {
  name: 'YardDetail',
  props: {
    // 是否显示模态框
    visible: {
      type: Boolean,
      default: false
    },
    // 堆场数据对象，包含堆场ID、贝数、排数等信息
    yardData: {
      type: Object,
      required: false,
      default: null,
      validator(value) {
        // 允许null值，但如果是对象则必须包含必要的属性
        return value === null || (typeof value === 'object' && value.yardId)
      }
    },
    // 集装箱数据 - 从YardBlock传入
    containerData: {
      type: Array,
      required: true
    },
    // 堆场计划数据 - 从YardBlock传入
    yardPlans: {
      type: Array,
      required: true
    },
    // 堆场加锁数据 - 从YardBlock传入
    yardLocks: {
      type: Array,
      required: true
    },
  },
  data() {
    return {
      // 当前操作模式：'view' | 'plan' | 'lock'
      currentMode: 'view',
      // 当前悬浮的贝号（用于查看模式下的整贝框选）
      hoveredBay: null,
      // 堆场配置参数
      config: {
        // 基础尺寸
        cellWidth: 60,        // 单元格宽度（像素）
        cellHeight: 30,       // 单元格高度（像素）

        // 标题尺寸（固定常量）
        bayNumberHeight: 30,  // 贝号高度（像素）
        rowNumberWidth: 40,   // 排号宽度（像素）

        // 显示控制
        intervalDisplayBays: false,  // 是否间隔显示贝号
        intervalDisplayRows: false,  // 是否间隔显示排号

        // 长方体尺寸偏移量
        cuboidOffset: 10,     // 长方体相对于单元格的偏移量（像素）
      },
      // 计划计划功能相关数据
      selectPlan: {
        isSelecting: false,     // 是否正在进行计划操作
        selectionStart: null,   // 计划开始位置的单元格坐标 {bay, row}
        selectionEnd: null,     // 计划结束位置的单元格坐标 {bay, row}
        selectedCells: new Set(), // 存储所有选中单元格的键值集合
      },
      // 加锁功能相关数据
      selectLock: {
        isSelecting: false,     // 是否正在进行加锁选择操作
        selectionStart: null,   // 选择开始位置的单元格坐标 {bay, row}
        selectionEnd: null,     // 选择结束位置的单元格坐标 {bay, row}
        lockedCells: new Set(), // 存储所有加锁单元格的键值集合
      },
      // 计划覆盖层悬停状态
      hoveredPlanId: null,    // 当前悬停的计划ID
      selectedPlanId: null,   // 当前选中的计划ID
      // 加锁覆盖层悬停状态
      hoveredLockId: null,    // 当前悬停的加锁ID
      selectedLockId: null,   // 当前选中的加锁ID
      // 计划拖拽调整状态
      planResizeState: {
        isResizing: false,        // 是否正在调整大小
        resizingPlanId: null,     // 正在调整的计划ID
        resizeDirection: null,    // 调整方向 ('nw', 'ne', 'sw', 'se')
        startPosition: { x: 0, y: 0 }, // 开始位置
        originalPlan: null,       // 原始计划数据
        currentPlan: null         // 当前计划数据
      },
      // 集装箱覆盖层悬停状态
      hoveredContainerId: null,    // 当前悬停的集装箱ID
      selectedContainerId: null,    // 当前选中的集装箱ID
      // 选择信息面板相关数据
      // 新增计划
      selectionInfo: null, // 选择区域的详细信息对象
      planDescription: '', // 计划描述
      isSavingPlan: false, // 是否正在保存计划
      saveSuccessMessage: '', // 保存成功提示消息
      showSaveSuccess: false, // 是否显示保存成功提示
      // 加锁信息面板相关数据
      lockInfo: null, // 加锁区域的详细信息对象
      lockDescription: '', // 加锁描述
      isSavingLock: false, // 是否正在保存加锁
      lockSuccessMessage: '', // 加锁成功提示消息
      showLockSuccess: false, // 是否显示加锁成功提示
      // 加载状态管理
      loadingState: {
        isDataLoading: false,        // 数据是否正在加载
        isRendering: false,          // 是否正在渲染
        isFullyLoaded: false,        // 是否完全加载完成
        renderProgress: 0,           // 渲染进度 (0-100)
        loadingMessage: '正在准备数据...', // 加载提示消息
        containerRenderBatch: 50,    // 每批渲染的集装箱数量
        currentBatch: 0,             // 当前渲染批次
        totalBatches: 0,             // 总批次数
        renderTimeout: null,         // 渲染超时定时器
        maxRenderTime: 10000,        // 最大渲染时间(毫秒)
      },
      // 渐进式渲染的集装箱数据
      renderedContainers: [],        // 已渲染的集装箱
      pendingContainers: [],         // 待渲染的集装箱
    }
  },
  computed: {
    /**
     * 是否显示加载状态
     * @returns {boolean} 是否显示加载状态
     */
    showLoadingState() {
      return this.loadingState.isDataLoading || this.loadingState.isRendering || !this.loadingState.isFullyLoaded
    },

    /**
     * 当前显示的集装箱数据（用于渐进式渲染）
     * @returns {Array} 当前应该显示的集装箱数组
     */
    displayContainers() {
      return this.renderedContainers
    },

    // 堆场号样式计算属性
    blockStyle() {
      if (!this.yardData) return {}
      return { height: `${this.config.bayNumberHeight}px`, width: `${this.config.rowNumberWidth}px` }
    },
    // 贝号列宽度样式计算属性
    bayHeaderStyle() {
      return { height: `${this.config.bayNumberHeight}px`, width: `${this.config.cellWidth}px` }
    },
    // 排号行高度样式计算属性
    rowHeaderStyle() {
      return { height: `${this.config.cellHeight}px`, width: `${this.config.rowNumberWidth}px` }
    },

    // 网格单元格样式计算属性，设置单元格的宽高和布局
    cellStyle() {
      return {
        width: `${this.config.cellWidth || 60}px`,
        height: `${this.config.cellHeight || 30}px`,
        flex: 'none'  // 防止单元格被拉伸
      }
    },
    // 根据集装箱类型返回不同的长方体配置
    getCuboidConfig() {
      return (container) => {
        // 计算长方体尺寸：单元格尺寸减去偏移量
        const width = this.config.cellWidth - this.config.cuboidOffset
        const height = this.config.cellHeight - this.config.cuboidOffset

        if (container.bay % 2 === 0) {
          // 偶数贝（大箱）- 宽度为小箱的2倍
          return {
            width: `${width * 2}px`,
            height: `${height}px`
          }
        } else {
          // 奇数贝（小箱）
          return {
            width: `${width}px`,
            height: `${height}px`
          }
        }
      }
    },
  },
  watch: {
    // 监听visible属性变化，控制加载流程
    visible: {
      handler(newVal) {
        if (newVal) {
          this.initializeLoading()
        } else {
          this.resetLoadingState()
        }
      },
      immediate: true
    },
    // 监听数据变化，触发渐进式渲染
    containerData: {
      handler(newData) {
        if (newData && newData.length > 0) {
          this.startProgressiveRendering(newData)
        }
      },
      immediate: true
    }
  },
  methods: {
    /**
     * 切换操作模式
     * @param {string} mode - 模式：'view' | 'plan' | 'lock'
     */
    switchMode(mode) {
      // 如果切换到非计划模式，清除当前计划选择
      if (mode !== 'plan' && this.selectPlan.isSelecting) {
        this.clearSelection()
      }
      
      // 如果切换到非加锁模式，清除当前加锁选择
      if (mode !== 'lock' && this.selectLock.isSelecting) {
        this.clearLockSelection()
      }
      
      this.currentMode = mode
      
      // 发射模式切换事件
      this.$emit('mode-changed', {
        yardId: this.yardData?.yardId,
        mode: mode
      })
    },

    /**
     * 获取网格的CSS类名
     * @returns {string} CSS类名字符串
     */
    getGridClass() {
      const classes = []
      
      // 根据当前模式添加相应的类
      switch (this.currentMode) {
        case 'view':
          classes.push('view-mode')
          break
        case 'plan':
          classes.push('plan-mode')
          break
        case 'lock':
          classes.push('lock-mode')
          break
      }
      
      return classes.join(' ')
    },

    /**
     * 初始化加载状态
     */
    initializeLoading() {
      // 确保默认为查看模式
      this.currentMode = 'view'
      
      this.loadingState.isDataLoading = true
      this.loadingState.isRendering = false
      this.loadingState.isFullyLoaded = false
      this.loadingState.renderProgress = 0
      this.loadingState.loadingMessage = '正在准备数据...'
      this.renderedContainers = []
      this.pendingContainers = []
    },
    /**
     * 重置加载状态
     */
    resetLoadingState() {
      // 清除渲染超时定时器
      if (this.loadingState.renderTimeout) {
        clearTimeout(this.loadingState.renderTimeout)
        this.loadingState.renderTimeout = null
      }

      this.loadingState.isDataLoading = false
      this.loadingState.isRendering = false
      this.loadingState.isFullyLoaded = false
      this.loadingState.renderProgress = 0
      this.loadingState.currentBatch = 0
      this.loadingState.totalBatches = 0
      this.renderedContainers = []
      this.pendingContainers = []

      // 清理样式缓存
      this._faceStylesCache = null
    },

    /**
     * 开始渐进式渲染
     * @param {Array} containers - 集装箱数据数组
     */
    async startProgressiveRendering(containers) {
      if (!containers || containers.length === 0) {
        this.loadingState.isDataLoading = false
        this.loadingState.isFullyLoaded = true
        return
      }

      // 设置渲染状态
      this.loadingState.isDataLoading = false
      this.loadingState.isRendering = true
      this.loadingState.loadingMessage = '正在渲染集装箱...'

      // 计算批次信息
      const batchSize = this.loadingState.containerRenderBatch
      this.loadingState.totalBatches = Math.ceil(containers.length / batchSize)
      this.loadingState.currentBatch = 0

      // 准备待渲染数据
      this.pendingContainers = [...containers]
      this.renderedContainers = []

      // 设置渲染超时保护
      this.loadingState.renderTimeout = setTimeout(() => {
        this.finishRendering()
      }, this.loadingState.maxRenderTime)

      // 开始分批渲染
      await this.renderNextBatch()
    },
    /**
     * 渲染下一批集装箱
     */
    async renderNextBatch() {
      if (this.pendingContainers.length === 0) {
        this.finishRendering()
        return
      }

      const batchSize = this.loadingState.containerRenderBatch
      const currentBatch = this.pendingContainers.splice(0, batchSize)

      // 添加到已渲染列表
      this.renderedContainers.push(...currentBatch)

      // 更新进度
      this.loadingState.currentBatch++
      this.loadingState.renderProgress = Math.round(
          (this.renderedContainers.length / (this.renderedContainers.length + this.pendingContainers.length)) * 100
      )

      // 强制更新视图
      this.$forceUpdate()

      // 使用requestAnimationFrame确保渲染完成后再继续
      await new Promise(resolve => {
        requestAnimationFrame(() => {
          requestAnimationFrame(resolve)
        })
      })

      // 继续渲染下一批
      if (this.pendingContainers.length > 0) {
        // 添加小延迟，避免阻塞UI
        setTimeout(() => {
          this.renderNextBatch()
        }, 16) // 约60fps
      } else {
        this.finishRendering()
      }
    },
    /**
     * 完成渲染
     */
    finishRendering() {
      // 清除超时定时器
      if (this.loadingState.renderTimeout) {
        clearTimeout(this.loadingState.renderTimeout)
        this.loadingState.renderTimeout = null
      }

      // 设置完成状态
      this.loadingState.isRendering = false
      this.loadingState.renderProgress = 100
      this.loadingState.loadingMessage = '加载完成'

      // 确保所有集装箱都已渲染
      this.renderedContainers = [...this.containerData]
      this.pendingContainers = []

      // 立即隐藏加载状态
      this.loadingState.isFullyLoaded = true
    },

    /**
     * 关闭模态框
     */
    closeModal() {
      // 清除所有选择状态和未保存的数据
      this.clearAllSelectionStates()

      this.$emit('close')
    },
    /**
     * 处理遮罩层点击
     */
    handleOverlayClick() {
      this.closeModal()
    },
    /**
     * 刷新数据
     */
    refreshData() {
      this.$emit('refresh')
    },

    /**
     * 判断是否应该显示指定贝号
     * @param {number} bay - 贝号
     * @returns {boolean} 是否显示该贝号
     */
    shouldShowBay(bay) {
      // 根据配置决定是否间隔显示贝号
      if (this.config.intervalDisplayBays) {
        // 间隔显示：只显示奇数贝号
        return bay % 2 === 1
      } else {
        // 连续显示：显示所有贝号
        return true
      }
    },
    /**
     * 判断是否应该显示指定排
     * @param {number} row - 排号
     * @returns {boolean} 是否显示该排
     */
    shouldShowRow(row) {
      // 根据配置决定是否间隔显示排号
      if (this.config.intervalDisplayRows) {
        // 间隔显示：只显示奇数排号
        return row % 2 === 1
      } else {
        // 连续显示：显示所有排号
        return true
      }
    },

    /**
     * 计算实际贝号（考虑方向）
     * @param {number} bay - 网格贝号
     * @returns {number} 实际贝号
     */
    calculateActualBay(bay) {
      if (!this.yardData) return bay
      let actualBay = bay * 2 - 1 // 贝号始终显示奇数（001, 003, 005...）

      if (this.yardData.bayDirection === 0) {
        // 沿负方向增加，从最大贝号开始显示
        const maxBay = this.yardData.bayCount * 2 - 1
        actualBay = maxBay - actualBay + 1
      }

      return actualBay
    },
    /**
     * 计算实际排号（考虑方向）
     * @param {number} row - 网格排号
     * @returns {number} 实际排号
     */
    calculateActualRow(row) {
      if (!this.yardData) return row
      let actualRow = row

      if (this.yardData.rowDirection === 0) {
        // 沿负方向增加，从最大排号开始显示
        actualRow = this.yardData.rowCount - row + 1
      }

      return actualRow
    },

    /**
     * 格式化贝号显示
     * @param {number} bay - 贝号
     * @returns {string} 格式化后的贝号（补零到3位）
     */
    formatBayNumber(bay) {
      if (!this.yardData) return ''
      return this.calculateActualBay(bay).toString().padStart(3, '0')
    },
    /**
     * 格式化排号显示
     * @param {number} row - 排号
     * @returns {string} 格式化后的排号（补零到2位）
     */
    formatRowNumber(row) {
      if (!this.yardData) return ''
      return this.calculateActualRow(row).toString().padStart(2, '0')
    },

    /**
     * 获取显示的贝号（用于data-col属性）
     * @param {number} bay - 网格贝号
     * @returns {number} 显示的贝号
     */
    getDisplayBay(bay) {
      if (!this.yardData) return bay
      return this.calculateActualBay(bay)
    },
    /**
     * 获取显示的排号（用于data-row属性）
     * @param {number} row - 网格排号
     * @returns {number} 显示的排号
     */
    getDisplayRow(row) {
      if (!this.yardData) return row
      return this.calculateActualRow(row)
    },

    /**
     * 获取集装箱覆盖层的样式类
     * @param {Object} container - 集装箱对象
     * @returns {string} CSS类名字符串
     */
    getContainerOverlayClass(container) {
      const classes = []

      // 添加偶数贝标识类
      if (container.bay % 2 === 0) {
        classes.push('even-bay-container')
      }

      // 添加悬停状态类
      const containerId = `${container.bay}-${container.row}-${container.tier || 1}`
      if (this.hoveredContainerId === containerId) {
        classes.push('container-hovered')
      }

      // 添加选中状态类
      if (this.selectedContainerId === containerId) {
        classes.push('container-selected')
      }

      return classes.join(' ')
    },
    /**
     * 获取计划覆盖层的样式类
     * @param {Object} plan - 计划对象
     * @returns {string} CSS类名字符串
     */
    getPlanOverlayClass(plan) {
      const classes = []
      classes.push('plan-overlay')

      // 添加悬停状态类
      if (this.hoveredPlanId === plan.planId) {
        classes.push('plan-hovered')
      }

      // 添加选中状态类
      if (this.selectedPlanId === plan.planId) {
        classes.push('plan-selected')
      }

      return classes.join(' ')
    },

    /**
     * 集装箱覆盖层的位置计算
     * @param {Object} container - 集装箱对象
     * @returns {Object} 样式对象
     */
    getContainerOverlayStyle(container) {
      // 计算集装箱位置
      const bay = container.bay
      const row = container.row

      // 网格边框宽度
      const yardGridBorder = 2
      // 排号宽度
      const rowNumberWidth = this.config.rowNumberWidth
      // 单元格高度
      const cellHeight = this.config.cellHeight

      let left, width

      // 检查是否是偶数贝集装箱
      if (bay % 2 === 0) {
        // 偶数贝集装箱占用两个相邻的奇数贝单元格
        // 例如：贝号10占用贝号9和贝号11的单元格
        const prevOddBay = bay - 1  // 前面的奇数贝

        // 计算前面奇数贝的网格列位置
        const prevGridCol = Math.floor((prevOddBay + 1) / 2)

        // 计算覆盖层位置（从前面奇数贝开始，宽度为两个单元格）
        left = rowNumberWidth + (prevGridCol - 1) * this.config.cellWidth
        width = this.config.cellWidth * 2 // 两个单元格的宽度
      } else {
        // 奇数贝集装箱只占用一个单元格
        const gridCol = Math.floor((bay + 1) / 2)
        left = rowNumberWidth + (gridCol - 1) * this.config.cellWidth
        width = this.config.cellWidth
      }

      // 根据贝循方向调整left位置
      if (this.yardData?.bayDirection === 0) {
        // 沿负方向增加，需要从右侧开始计算
        const totalWidth = this.yardData.bayCount * this.config.cellWidth
        left = (rowNumberWidth + totalWidth) - (left - rowNumberWidth) - width
      }

      // 根据排循方向调整top位置(表头线的高度 + 单元格总高度 + 单元格线的总高度)
      let top = yardGridBorder +
          ((row - 1) * cellHeight) +
          ((row - 2) * 0.6)

      if (this.yardData?.rowDirection === 0) {
        // 沿负方向增加，需要从底部开始计算(堆场总高度 - 距离底部高度 + 单元格线总高度)
        const totalHeight = this.yardData.rowCount * cellHeight
        top = (yardGridBorder + totalHeight) - row * cellHeight + ((this.yardData.rowCount - row) * 0.6)
      }

      // 根据Y坐标设置z-index，Y坐标小的在前面（更高的z-index）
      const zIndex = Math.floor(top)

      // 基础样式对象
      const baseStyle = {
        position: 'absolute',
        left: `${left}px`,
        top: `${top}px`,
        width: `${width}px`,
        height: `${cellHeight}px`,
        zIndex: zIndex
      }

      // 移除所有覆盖层样式
      baseStyle.background = 'transparent'

      return baseStyle
    },
    /**
     * 计划覆盖层的位置计算
     * @param {Object} plan - 计划对象
     * @returns {Object} 样式对象
     */
    getPlanOverlayStyle(plan) {
      // 计算计划区域的位置和尺寸
      const startBay = plan.startBay
      const endBay = plan.endBay
      const startRow = plan.startRow
      const endRow = plan.endRow

      // 将实际贝号转换为网格列位置
      // 实际贝号1,3,5,7...对应网格列1,2,3,4...
      const startGridCol = Math.floor((startBay + 1) / 2)
      const endGridCol = Math.floor((endBay + 1) / 2)

      // 网格边框宽度
      const yardGridBorder = 2
      // 排号宽度
      const rowNumberWidth = this.config.rowNumberWidth
      // 单元格高度
      const cellHeight = this.config.cellHeight

      // 计算覆盖层的实际范围（使用最小值和最大值）
      const minGridCol = Math.min(startGridCol, endGridCol)
      const maxGridCol = Math.max(startGridCol, endGridCol)
      const minRow = Math.min(startRow, endRow)
      const maxRow = Math.max(startRow, endRow)

      // 使用网格列位置计算覆盖层位置
      let left = rowNumberWidth +
          (minGridCol - 1) * this.config.cellWidth
      let top = yardGridBorder +
          ((minRow - 1) * this.config.cellHeight) +
          ((minRow - 2) * 0.6)
      const width = (maxGridCol - minGridCol + 1) * this.config.cellWidth
      const height = (maxRow - minRow + 1) * this.config.cellHeight

      // 根据贝循方向调整left位置
      if (this.yardData?.bayDirection === 0) {
        // 沿负方向增加，需要从右侧开始计算
        const totalWidth = this.yardData.bayCount * this.config.cellWidth
        left = (rowNumberWidth + totalWidth) - (left - rowNumberWidth) - width
      }

      // 根据排循方向调整top位置
      if (this.yardData?.rowDirection === 0) {
        // 沿负方向增加，需要从底部开始计算(堆场总高度 - 距离底部高度 - 自身高度 + 单元格线总高度)
        const totalHeight = this.yardData.rowCount * this.config.cellHeight
        top = (yardGridBorder + totalHeight) - ((startRow - 1) * cellHeight) - height + ((this.yardData.rowCount - endRow) * 0.6)
      }

      // 基础样式对象
      const baseStyle = {
        position: 'absolute',
        left: `${left}px`,
        top: `${top}px`,
        width: `${width}px`,
        height: `${height}px`,
        zIndex: 2
      }

      // 如果计划有颜色，添加动态颜色样式
      if (plan.color) {
        const color = plan.color
        const rgb = this.hexToRgb(color)

        if (rgb) {
          // 检查状态
          const isHovered = this.hoveredPlanId === plan.planId
          const isSelected = this.selectedPlanId === plan.planId
          
          if (isSelected) {
            // 选中状态：使用纯色背景，增强边框和阴影
            baseStyle.background = `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.4)`
            baseStyle.borderColor = `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.9)`
            baseStyle.boxShadow = `
              0 8px 25px rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.4),
              0 4px 15px rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.3),
              0 2px 8px rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.2),
              0 0 0 2px rgba(255, 255, 255, 0.4)
            `
            // 设置CSS变量用于脉冲动画
            baseStyle['--pulse-color'] = color
            baseStyle['--pulse-rgb'] = `${rgb.r}, ${rgb.g}, ${rgb.b}`
          } else if (isHovered) {
            // 悬停时使用纯色背景
            baseStyle.background = `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.3)`
            baseStyle.borderColor = `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.8)`
            // 优化阴影效果：多层阴影营造立体感
            baseStyle.boxShadow = `
              0 12px 35px rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.25),
              0 6px 20px rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.15),
              0 2px 8px rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.1),
              0 0 0 1px rgba(255, 255, 255, 0.3),
              inset 0 1px 0 rgba(255, 255, 255, 0.2)
            `
          } else {
            // 正常状态使用斜纹背景
            baseStyle.background = `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.05) repeating-linear-gradient(
              45deg,
              transparent,
              transparent 6px,
              rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.2) 6px,
              rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.2) 8px
            )`
            baseStyle.backgroundSize = '8px 8px'
            // 优化正常状态阴影：更柔和的阴影效果
            baseStyle.boxShadow = `
              0 4px 12px rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.12),
              0 2px 6px rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.08),
              0 1px 3px rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.05)
            `
          }
        }
      }

      return baseStyle
    },

    /**
     * 获取单元格的CSS类名
     * 根据选择状态返回相应的样式类（集装箱现在通过浮层显示）
     * @param {number} bay - 贝号
     * @param {number} row - 排号
     * @returns {string} CSS类名字符串
     */
    getCellClass(bay, row) {
      if (!this.yardData) return ''
      // 使用公共方法计算实际贝号和排号
      const actualBay = this.calculateActualBay(bay)
      const actualRow = this.calculateActualRow(row)

      // 生成单元格的唯一标识（使用实际贝号）
      const cellKey = `${actualBay}-${actualRow}`
      // 检查该单元格是否被选中（计划模式）
      const isSelected = this.selectPlan.selectedCells.has(cellKey)
      // 检查该单元格是否被加锁
      const isLocked = this.selectLock.lockedCells.has(cellKey)
      
      // 检查是否在查看模式下悬浮贝号
      const isBayHovered = this.currentMode === 'view' && this.hoveredBay === bay

      // 构建CSS类名
      const classes = []
      if (isSelected) classes.push('selected')
      if (isLocked) classes.push('locked')
      if (isBayHovered) classes.push('bay-hovered')
      
      return classes.join(' ')
    },

    /**
     * 开始选择堆场计划操作
     * 处理鼠标按下事件，初始化选择状态
     * @param {MouseEvent} event - 鼠标事件对象
     */
    startSelection(event) {
      // 只在计划模式或加锁模式下启用选择功能
      if (this.currentMode !== 'plan' && this.currentMode !== 'lock') {
        return
      }

      // 防止在标题或其他非网格区域开始选择
      const forbiddenSelectors = [
        '.bay-header',
        '.column-header',
        '.bay-number',
        '.yard-title'
      ]

      const isForbiddenArea = forbiddenSelectors.some(selector =>
          event.target.closest(selector)
      )

      if (isForbiddenArea) {
        return
      }

      // 如果点击的是网格单元格，开始选择
      if (event.target.classList.contains('grid-cell')) {
        // 获取单元格的贝排坐标
        const bay = parseInt(event.target.dataset.col)
        const row = parseInt(event.target.dataset.row)

        if (this.currentMode === 'plan') {
          // 计划模式选择
          this.selectPlan.isSelecting = true
          this.selectPlan.selectionStart = { bay, row }
          this.selectPlan.selectionEnd = { bay, row }
          // 立即更新选择显示
          this.updateSelectedCells()
        } else if (this.currentMode === 'lock') {
          // 加锁模式选择
          this.selectLock.isSelecting = true
          this.selectLock.selectionStart = { bay, row }
          this.selectLock.selectionEnd = { bay, row }
          // 立即更新加锁显示
          this.updateLockedCells()
        }

        // 阻止默认的鼠标行为
        event.preventDefault()
      }
    },
    /**
     * 结束选择堆场计划操作
     * 处理鼠标释放事件，完成选择并发射事件给父组件
     */
    endSelection() {
      // 处理计划模式选择结束
      if (this.selectPlan.isSelecting) {
        // 结束选择状态
        this.selectPlan.isSelecting = false

        // 计算选择区域信息并更新本地状态
        const selectionInfo = this.calculateSelectionInfo()
        if (selectionInfo) {
          this.selectionInfo = selectionInfo
          // 发射事件给父组件
          this.$emit('selection-changed', {
            yardId: this.yardData?.yardId,
            selectionInfo: selectionInfo,
            selectedCells: Array.from(this.selectPlan.selectedCells)
          })
        }
      }

      // 处理加锁模式选择结束
      if (this.selectLock.isSelecting) {
        // 结束选择状态
        this.selectLock.isSelecting = false

        // 计算加锁区域信息并更新本地状态
        const lockInfo = this.calculateLockInfo()
        if (lockInfo) {
          this.lockInfo = lockInfo
          // 发射事件给父组件
          this.$emit('lock-changed', {
            yardId: this.yardData?.yardId,
            lockInfo: lockInfo,
            lockedCells: Array.from(this.selectLock.lockedCells)
          })
        }
      }
    },
    /**
     * 鼠标进入单元格事件处理(每次进入单元格都会触发)
     * 在选择状态下，更新选择结束位置并重新计算选中区域
     * @param {MouseEvent} event - 鼠标事件对象
     */
    onCellMouseEnter(event) {
      // 处理计划模式选择
      if (this.currentMode === 'plan' && this.selectPlan.isSelecting) {
        // 获取当前单元格的贝排坐标
        const bay = parseInt(event.target.dataset.col)
        const row = parseInt(event.target.dataset.row)

        // 更新选择结束位置
        this.selectPlan.selectionEnd = { bay, row }
        // 重新计算选中的单元格
        this.updateSelectedCells()
      }

      // 处理加锁模式选择
      if (this.currentMode === 'lock' && this.selectLock.isSelecting) {
        // 获取当前单元格的贝排坐标
        const bay = parseInt(event.target.dataset.col)
        const row = parseInt(event.target.dataset.row)

        // 更新选择结束位置
        this.selectLock.selectionEnd = { bay, row }
        // 重新计算加锁的单元格
        this.updateLockedCells()
      }
    },

    /**
     * 贝号头部悬浮事件处理
     * @param {number} bay - 贝号
     */
    onBayHeaderHover(bay) {
      if (this.currentMode === 'view') {
        this.hoveredBay = bay
      }
    },

    /**
     * 贝号头部悬浮离开事件处理
     */
    onBayHeaderLeave() {
      if (this.currentMode === 'view') {
        this.hoveredBay = null
      }
    },

    /**
     * 显示单贝图
     * 双击贝号或单元格时调用
     * @param {number} bay - 贝号
     * @param {number} row - 排号（可选，用于日志记录）
     */
    showBayViewer(bay, row = null) {
      if (this.currentMode === 'view') {

        // 计算实际贝号
        const actualBay = this.calculateActualBay(bay)

        // 发射事件给父组件，显示单贝图
        this.$emit('show-bay-viewer', {
          yardId: this.yardData?.yardId,
          bayNumber: actualBay,
          bayCount: this.yardData?.bayCount || 0,
          rowCount: this.yardData?.rowCount || 0,
          tierCount: this.yardData?.tierCount || 1,
          containerData: this.containerData,
          yardPlans: this.yardPlans,
          yardLocks: this.yardLocks
        })

        console.log('显示贝图:', {
          yardId: this.yardData?.yardId,
          bayNumber: actualBay,
          row: row,
          source: row ? '单元格双击' : '贝号双击'
        })
      }
    },


    /**
     * 计算选择范围
     * 根据选择开始和结束位置计算有效的选择范围
     * 保持实际绘制顺序，不强制使用min/max
     * @returns {Object} 包含有效选择范围的对象
     */
    calculateSelectionRange() {
      if (!this.selectPlan.selectionStart || !this.selectPlan.selectionEnd) return null

      // 保持实际绘制顺序的开始和结束值
      // 注意：selectionStart.bay 和 selectionEnd.bay 已经是实际贝号（来自 data-col）
      let startBay = this.selectPlan.selectionStart.bay
      let endBay = this.selectPlan.selectionEnd.bay
      let startRow = this.selectPlan.selectionStart.row
      let endRow = this.selectPlan.selectionEnd.row

      // 根据方向字段调整选择范围
      if (!this.yardData) return null
      if (this.yardData.bayDirection === 0) {
        // 贝循方向为负方向，需要反转贝号
        const maxActualBay = this.yardData.bayCount * 2 - 1
        const tempStartBay = startBay
        startBay = maxActualBay - endBay + 1
        endBay = maxActualBay - tempStartBay + 1
      }

      if (this.yardData.rowDirection === 0) {
        // 排循方向为负方向，需要反转排号
        const tempStartRow = startRow
        startRow = this.yardData.rowCount - endRow + 1
        endRow = this.yardData.rowCount - tempStartRow + 1
      }

      // 计算最大可能的实际贝号（基于bayCount）
      // bayCount=20 意味着网格有20列，对应实际贝号 1,3,5,7,...,39
      const maxActualBay = this.yardData.bayCount * 2 - 1

      // 确保在有效范围内
      return {
        startBay: Math.max(1, Math.min(maxActualBay, startBay)),
        endBay: Math.max(1, Math.min(maxActualBay, endBay)),
        startRow: Math.max(1, Math.min(this.yardData.rowCount, startRow)),
        endRow: Math.max(1, Math.min(this.yardData.rowCount, endRow))
      }
    },
    /**
     * 更新选中的单元格
     * 根据选择开始和结束位置计算并更新选中的单元格集合
     */
    updateSelectedCells() {
      const range = this.calculateSelectionRange()
      if (!range) return

      // 清空之前的选择
      this.selectPlan.selectedCells.clear()

      // 计算实际的贝号范围（确保包含所有奇数贝号）
      const minBay = Math.min(range.startBay, range.endBay)
      const maxBay = Math.max(range.startBay, range.endBay)
      const minRow = Math.min(range.startRow, range.endRow)
      const maxRow = Math.max(range.startRow, range.endRow)

      // 添加新的选中单元格
      // 遍历所有奇数贝号
      for (let bay = minBay; bay <= maxBay; bay += 2) {
        for (let row = minRow; row <= maxRow; row++) {
          // 根据方向字段调整单元格键值
          if (!this.yardData) continue
          let displayBay = bay
          let displayRow = row

          if (this.yardData.bayDirection === 0) {
            // 贝循方向为负方向，需要反转贝号
            const maxActualBay = this.yardData.bayCount * 2 - 1
            displayBay = maxActualBay - bay + 1
          }

          if (this.yardData.rowDirection === 0) {
            // 排循方向为负方向，需要反转排号
            displayRow = this.yardData.rowCount - row + 1
          }

          const cellKey = `${displayBay}-${displayRow}`
          this.selectPlan.selectedCells.add(cellKey)
        }
      }

      // 强制更新视图
      this.$forceUpdate()
    },
    /**
     * 更新加锁的单元格
     * 根据加锁开始和结束位置计算并更新加锁的单元格集合
     */
    updateLockedCells() {
      const range = this.calculateLockRange()
      if (!range) return

      // 清空之前的加锁
      this.selectLock.lockedCells.clear()

      // 计算实际的贝号范围（确保包含所有奇数贝号）
      const minBay = Math.min(range.startBay, range.endBay)
      const maxBay = Math.max(range.startBay, range.endBay)
      const minRow = Math.min(range.startRow, range.endRow)
      const maxRow = Math.max(range.startRow, range.endRow)

      // 添加新的加锁单元格
      // 遍历所有奇数贝号
      for (let bay = minBay; bay <= maxBay; bay += 2) {
        for (let row = minRow; row <= maxRow; row++) {
          // 根据方向字段调整单元格键值
          if (!this.yardData) continue
          let displayBay = bay
          let displayRow = row

          if (this.yardData.bayDirection === 0) {
            // 贝循方向为负方向，需要反转贝号
            const maxActualBay = this.yardData.bayCount * 2 - 1
            displayBay = maxActualBay - bay + 1
          }

          if (this.yardData.rowDirection === 0) {
            // 排循方向为负方向，需要反转排号
            displayRow = this.yardData.rowCount - row + 1
          }

          const cellKey = `${displayBay}-${displayRow}`
          this.selectLock.lockedCells.add(cellKey)
        }
      }

      // 强制更新视图
      this.$forceUpdate()
    },
    /**
     * 计算加锁范围
     * 根据加锁开始和结束位置计算有效的加锁范围
     * 保持实际绘制顺序，不强制使用min/max
     * @returns {Object} 包含有效加锁范围的对象
     */
    calculateLockRange() {
      if (!this.selectLock.selectionStart || !this.selectLock.selectionEnd) return null

      // 保持实际绘制顺序的开始和结束值
      // 注意：selectionStart.bay 和 selectionEnd.bay 已经是实际贝号（来自 data-col）
      let startBay = this.selectLock.selectionStart.bay
      let endBay = this.selectLock.selectionEnd.bay
      let startRow = this.selectLock.selectionStart.row
      let endRow = this.selectLock.selectionEnd.row

      // 根据方向字段调整选择范围
      if (!this.yardData) return null
      if (this.yardData.bayDirection === 0) {
        // 贝循方向为负方向，需要反转贝号
        const maxActualBay = this.yardData.bayCount * 2 - 1
        const tempStartBay = startBay
        startBay = maxActualBay - endBay + 1
        endBay = maxActualBay - tempStartBay + 1
      }

      if (this.yardData.rowDirection === 0) {
        // 排循方向为负方向，需要反转排号
        const tempStartRow = startRow
        startRow = this.yardData.rowCount - endRow + 1
        endRow = this.yardData.rowCount - tempStartRow + 1
      }

      // 计算最大可能的实际贝号（基于bayCount）
      // bayCount=20 意味着网格有20列，对应实际贝号 1,3,5,7,...,39
      const maxActualBay = this.yardData.bayCount * 2 - 1

      // 确保在有效范围内
      return {
        startBay: Math.max(1, Math.min(maxActualBay, startBay)),
        endBay: Math.max(1, Math.min(maxActualBay, endBay)),
        startRow: Math.max(1, Math.min(this.yardData.rowCount, startRow)),
        endRow: Math.max(1, Math.min(this.yardData.rowCount, endRow))
      }
    },
    /**
     * 计算加锁区域信息
     * 根据加锁的单元格计算加锁区域的详细信息
     * 使用实际绘制顺序的开始和结束值
     * @returns {Object|null} 加锁区域信息对象
     */
    calculateLockInfo() {
      if (this.selectLock.lockedCells.size === 0) {
        return null
      }

      // 使用实际绘制顺序的开始和结束值
      const range = this.calculateLockRange()
      if (!range) return null

      const startBay = range.startBay
      const endBay = range.endBay
      const startRow = range.startRow
      const endRow = range.endRow

      // 计算加锁区域的尺寸描述
      const minBay = Math.min(startBay, endBay)
      const maxBay = Math.max(startBay, endBay)
      const minRow = Math.min(startRow, endRow)
      const maxRow = Math.max(startRow, endRow)

      const bayCount = Math.floor((maxBay - minBay) / 2) + 1
      const rowCount = maxRow - minRow + 1
      const areaDescription = `${bayCount}贝 × ${rowCount}排`

      return {
        yardId: this.yardData?.yardId,
        startBay: startBay,
        endBay: endBay,
        startRow: startRow,
        endRow: endRow,
        areaDescription: areaDescription,
        lockedCount: this.selectLock.lockedCells.size
      }
    },
    /**
     * 计算选择区域信息
     * 根据选中的单元格计算选择区域的详细信息
     * 使用实际绘制顺序的开始和结束值
     * @returns {Object|null} 选择区域信息对象
     */
    calculateSelectionInfo() {
      if (this.selectPlan.selectedCells.size === 0) {
        return null
      }

      // 使用实际绘制顺序的开始和结束值
      const range = this.calculateSelectionRange()
      if (!range) return null

      const startBay = range.startBay
      const endBay = range.endBay
      const startRow = range.startRow
      const endRow = range.endRow

      // 计算选择区域的尺寸描述
      const minBay = Math.min(startBay, endBay)
      const maxBay = Math.max(startBay, endBay)
      const minRow = Math.min(startRow, endRow)
      const maxRow = Math.max(startRow, endRow)

      const bayCount = Math.floor((maxBay - minBay) / 2) + 1
      const rowCount = maxRow - minRow + 1
      const areaDescription = `${bayCount}贝 × ${rowCount}排`

      return {
        yardId: this.yardData?.yardId,
        startBay: startBay,
        endBay: endBay,
        startRow: startRow,
        endRow: endRow,
        areaDescription: areaDescription,
        selectedCount: this.selectPlan.selectedCells.size
      }
    },
    /**
     * 清除选择堆场计划
     * 重置所有选择相关的状态和数据，并发射事件给父组件
     */
    clearSelection() {
      this.selectPlan.selectedCells.clear()
      this.selectPlan.isSelecting = false
      this.selectPlan.selectionStart = null
      this.selectPlan.selectionEnd = null
      this.selectionInfo = null

      // 同时清除计划样式
      this.hoveredPlanId = null
      this.selectedPlanId = null
      this.hoveredContainerId = null
      this.selectedContainerId = null

      // 发射清除选择事件给父组件
      this.$emit('selection-cleared', {
        yardId: this.yardData?.yardId
      })

      // 强制更新视图以清除选择样式
      this.$forceUpdate()
    },
    /**
     * 清除加锁选择
     * 重置所有加锁相关的状态和数据，并发射事件给父组件
     */
    clearLockSelection() {
      this.selectLock.lockedCells.clear()
      this.selectLock.isSelecting = false
      this.selectLock.selectionStart = null
      this.selectLock.selectionEnd = null
      this.lockInfo = null

      // 发射清除加锁事件给父组件
      this.$emit('lock-cleared', {
        yardId: this.yardData?.yardId
      })

      // 强制更新视图以清除加锁样式
      this.$forceUpdate()
    },

    /**
     * 清除所有选择状态和未保存的数据
     * 在关闭模态框时调用，确保下次打开时是干净的状态
     */
    clearAllSelectionStates() {
      // 重置为查看模式
      this.currentMode = 'view'
      
      // 清除贝号悬浮状态
      this.hoveredBay = null
      
      // 清除选择计划相关状态
      this.selectPlan.selectedCells.clear()
      this.selectPlan.isSelecting = false
      this.selectPlan.selectionStart = null
      this.selectPlan.selectionEnd = null
      this.selectionInfo = null

      // 清除加锁相关状态
      this.selectLock.lockedCells.clear()
      this.selectLock.isSelecting = false
      this.selectLock.selectionStart = null
      this.selectLock.selectionEnd = null
      this.lockInfo = null

      // 清除计划描述输入框
      this.planDescription = ''
      // 清除加锁描述输入框
      this.lockDescription = ''

      // 清除悬停和选中状态
      this.hoveredPlanId = null
      this.selectedPlanId = null
      this.hoveredContainerId = null
      this.selectedContainerId = null

      // 清除保存成功提示
      this.showSaveSuccess = false
      this.saveSuccessMessage = ''
      // 清除加锁成功提示
      this.showLockSuccess = false
      this.lockSuccessMessage = ''

      // 重置加载状态
      this.resetLoadingState()

      // 强制更新视图
      this.$forceUpdate()
    },

    /**
     * 保存当前选择区域为堆场计划
     * 这是一个便捷方法，用于快速保存当前选择区域
     * @param {string} description - 计划描述
     */
    async saveCurrentSelectionAsPlan(description = '') {
      if (this.selectPlan.selectedCells.size === 0) {
        return null
      }

      const selectionInfo = this.calculateSelectionInfo()
      if (!selectionInfo) {
        return null
      }

      const planData = {
        startBay: selectionInfo.startBay,
        endBay: selectionInfo.endBay,
        startRow: selectionInfo.startRow,
        endRow: selectionInfo.endRow,
        description: description || this.planDescription || selectionInfo.areaDescription
      }

      return await this.saveYardPlan(planData)
    },
    /**
     * 保存当前加锁区域
     * 这是一个便捷方法，用于快速保存当前加锁区域
     * @param {string} description - 加锁描述
     */
    async saveCurrentLockAsArea(description = '') {
      if (this.selectLock.lockedCells.size === 0) {
        return null
      }

      const lockInfo = this.calculateLockInfo()
      if (!lockInfo) {
        return null
      }

      const lockData = {
        startBay: lockInfo.startBay,
        endBay: lockInfo.endBay,
        startRow: lockInfo.startRow,
        endRow: lockInfo.endRow,
        description: description || this.lockDescription || lockInfo.areaDescription
      }

      return await this.saveYardLock(lockData)
    },
    /**
     * 保存堆场计划
     * 将当前选择区域保存为新的堆场计划
     * @param {Object} planData - 计划数据对象
     */
    async saveYardPlan(planData) {
      this.isSavingPlan = true

      try {
        // 使用API服务保存计划
        const newPlan = await dataApiService.saveYardPlan({
          yardId: this.yardData?.yardId,
          startBay: planData.startBay,
          endBay: planData.endBay,
          startRow: planData.startRow,
          endRow: planData.endRow,
          description: planData.description || `${this.yardData?.yardId}堆场${planData.startBay}-${planData.endBay}贝${planData.startRow}-${planData.endRow}排区域`,
          color: '#4CAF50' // 默认绿色
        })

        // 显示保存成功提示
        this.showSaveSuccessMessage(newPlan)

        // 清空描述输入框
        this.planDescription = ''
        // 清除选择状态，让用户看到保存的计划区域
        this.clearSelection()

        // 发射事件给父组件，让父组件更新数据
        this.$emit('plan-saved', {
          yardId: this.yardData?.yardId,
          plan: newPlan
        })

        return newPlan
      } catch (error) {
        console.error('保存计划失败:', error)
        alert('保存计划失败: ' + error.message)
        throw error
      } finally {
        this.isSavingPlan = false
      }
    },
    /**
     * 保存堆场加锁区域
     * 将当前加锁区域保存为新的加锁区域
     * @param {Object} lockData - 加锁数据对象
     */
    async saveYardLock(lockData) {
      this.isSavingLock = true

      try {
        // 使用API服务保存加锁
        const newLock = await dataApiService.saveYardLock({
          yardId: this.yardData?.yardId,
          startBay: lockData.startBay,
          endBay: lockData.endBay,
          startRow: lockData.startRow,
          endRow: lockData.endRow,
          description: lockData.description || `${this.yardData?.yardId}堆场${lockData.startBay}-${lockData.endBay}贝${lockData.startRow}-${lockData.endRow}排区域`,
          color: '#FF9800' // 默认橙色
        })

        // 显示保存成功提示
        this.showLockSuccessMessage(newLock)

        // 清空描述输入框
        this.lockDescription = ''
        // 清除加锁选择状态，让用户看到保存的加锁区域
        this.clearLockSelection()

        // 发射事件给父组件，让父组件更新数据
        this.$emit('lock-saved', {
          yardId: this.yardData?.yardId,
          lock: newLock
        })

        return newLock
      } catch (error) {
        console.error('保存加锁区域失败:', error)
        alert('保存加锁区域失败: ' + error.message)
        throw error
      } finally {
        this.isSavingLock = false
      }
    },

    /**
     * 显示保存成功提示
     * @param {Object} plan - 保存的计划对象
     */
    showSaveSuccessMessage(plan) {
      this.saveSuccessMessage = `计划保存成功！\n计划ID: ${plan.planId}\n区域: ${plan.yardId}堆场${plan.startBay}-${plan.endBay}贝${plan.startRow}-${plan.endRow}排`
      this.showSaveSuccess = true

      // 3秒后自动隐藏提示
      setTimeout(() => {
        this.hideSaveSuccess()
      }, 3000)
    },

    /**
     * 隐藏保存成功提示
     */
    hideSaveSuccess() {
      this.showSaveSuccess = false
      this.saveSuccessMessage = ''
    },

    /**
     * 显示加锁成功提示
     * @param {Object} lock - 保存的加锁对象
     */
    showLockSuccessMessage(lock) {
      this.lockSuccessMessage = `加锁区域保存成功！\n加锁ID: ${lock.lockId}\n区域: ${lock.yardId}堆场${lock.startBay}-${lock.endBay}贝${lock.startRow}-${lock.endRow}排`
      this.showLockSuccess = true

      // 3秒后自动隐藏提示
      setTimeout(() => {
        this.hideLockSuccess()
      }, 3000)
    },

    /**
     * 隐藏加锁成功提示
     */
    hideLockSuccess() {
      this.showLockSuccess = false
      this.lockSuccessMessage = ''
    },

    /**
     * 处理计划覆盖层鼠标进入事件
     * @param {Object} plan - 计划对象
     */
    onPlanOverlayHover(plan) {
      this.hoveredPlanId = plan.planId
    },
    /**
     * 处理计划覆盖层鼠标离开事件
     */
    onPlanOverlayLeave() {
      this.hoveredPlanId = null
    },
    /**
     * 处理计划覆盖层点击事件
     * @param {Object} plan - 计划对象
     */
    onPlanOverlayClick(plan) {
      // 切换选中状态
      if (this.selectedPlanId === plan.planId) {
        this.selectedPlanId = null
      } else {
        this.selectedPlanId = plan.planId
      }

      // 发射事件给父组件
      this.$emit('plan-selected', {
        yardId: this.yardData?.yardId,
        plan: plan,
        isSelected: this.selectedPlanId === plan.planId
      })
    },

    /**
     * 获取加锁覆盖层的样式类
     * @param {Object} lock - 加锁对象
     * @returns {string} CSS类名字符串
     */
    getLockOverlayClass(lock) {
      const classes = []
      classes.push('lock-overlay')

      // 添加悬停状态类
      if (this.hoveredLockId === lock.lockId) {
        classes.push('lock-hovered')
      }

      // 添加选中状态类
      if (this.selectedLockId === lock.lockId) {
        classes.push('lock-selected')
      }

      return classes.join(' ')
    },

    /**
     * 加锁覆盖层的位置计算
     * @param {Object} lock - 加锁对象
     * @returns {Object} 样式对象
     */
    getLockOverlayStyle(lock) {
      // 计算加锁区域的位置和尺寸
      const bay = lock.bay
      const row = lock.row

      // 将实际贝号转换为网格列位置
      // 实际贝号1,3,5,7...对应网格列1,2,3,4...
      const gridCol = Math.floor((bay + 1) / 2)

      // 网格边框宽度
      const yardGridBorder = 2
      // 排号宽度
      const rowNumberWidth = this.config.rowNumberWidth
      // 单元格高度
      const cellHeight = this.config.cellHeight

      // 使用网格列位置计算覆盖层位置
      let left = rowNumberWidth +
          (gridCol - 1) * this.config.cellWidth
      let top = yardGridBorder +
          ((row - 1) * this.config.cellHeight) +
          ((row - 2) * 0.6)
      const width = this.config.cellWidth
      const height = this.config.cellHeight

      // 根据贝循方向调整left位置
      if (this.yardData?.bayDirection === 0) {
        // 沿负方向增加，需要从右侧开始计算
        const totalWidth = this.yardData.bayCount * this.config.cellWidth
        left = (rowNumberWidth + totalWidth) - (left - rowNumberWidth) - width
      }

      // 根据排循方向调整top位置
      if (this.yardData?.rowDirection === 0) {
        // 沿负方向增加，需要从底部开始计算(堆场总高度 - 距离底部高度 - 自身高度 + 单元格线总高度)
        const totalHeight = this.yardData.rowCount * this.config.cellHeight
        top = (yardGridBorder + totalHeight) - ((row - 1) * cellHeight) - height + ((this.yardData.rowCount - row) * 0.6)
      }

      // 基础样式对象
      const baseStyle = {
        position: 'absolute',
        left: `${left}px`,
        top: `${top}px`,
        width: `${width}px`,
        height: `${height}px`,
        zIndex: 2
      }

      // 如果加锁有颜色，添加动态颜色样式
      if (lock.color) {
        const color = lock.color
        const rgb = this.hexToRgb(color)

        if (rgb) {
          // 检查状态
          const isHovered = this.hoveredLockId === lock.lockId
          const isSelected = this.selectedLockId === lock.lockId
          
          if (isSelected) {
            // 选中状态：使用纯色背景，增强边框和阴影
            baseStyle.background = `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.4)`
            baseStyle.borderColor = `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.9)`
            baseStyle.boxShadow = `
              0 8px 25px rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.4),
              0 4px 15px rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.3),
              0 2px 8px rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.2),
              0 0 0 2px rgba(255, 255, 255, 0.4)
            `
            // 设置CSS变量用于脉冲动画
            baseStyle['--pulse-color'] = color
            baseStyle['--pulse-rgb'] = `${rgb.r}, ${rgb.g}, ${rgb.b}`
          } else if (isHovered) {
            // 悬停时使用纯色背景
            baseStyle.background = `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.3)`
            baseStyle.borderColor = `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.8)`
            // 优化阴影效果：多层阴影营造立体感
            baseStyle.boxShadow = `
              0 12px 35px rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.25),
              0 6px 20px rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.15),
              0 2px 8px rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.1),
              0 0 0 1px rgba(255, 255, 255, 0.3),
              inset 0 1px 0 rgba(255, 255, 255, 0.2)
            `
          } else {
            // 正常状态使用纯色背景
            baseStyle.background = `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.1)`
            baseStyle.borderColor = `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.6)`
            // 优化正常状态阴影：更柔和的阴影效果
            baseStyle.boxShadow = `
              0 4px 12px rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.12),
              0 2px 6px rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.08),
              0 1px 3px rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.05)
            `
          }
        }
      }

      return baseStyle
    },

    /**
     * 处理加锁覆盖层鼠标进入事件
     * @param {Object} lock - 加锁对象
     */
    onLockOverlayHover(lock) {
      this.hoveredLockId = lock.lockId
    },
    /**
     * 处理加锁覆盖层鼠标离开事件
     */
    onLockOverlayLeave() {
      this.hoveredLockId = null
    },
    /**
     * 处理加锁覆盖层点击事件
     * @param {Object} lock - 加锁对象
     */
    onLockOverlayClick(lock) {
      // 切换选中状态
      if (this.selectedLockId === lock.lockId) {
        this.selectedLockId = null
      } else {
        this.selectedLockId = lock.lockId
      }

      // 发射事件给父组件
      this.$emit('lock-selected', {
        yardId: this.yardData?.yardId,
        lock: lock,
        isSelected: this.selectedLockId === lock.lockId
      })
    },

    /**
     * 开始调整计划大小
     * @param {MouseEvent} event - 鼠标事件
     * @param {Object} plan - 计划对象
     * @param {string} direction - 调整方向 ('nw', 'ne', 'sw', 'se')
     */
    startResize(event, plan, direction) {
      // 阻止事件冒泡
      event.preventDefault()
      event.stopPropagation()

      // 设置调整状态
      this.planResizeState.isResizing = true
      this.planResizeState.resizingPlanId = plan.planId
      this.planResizeState.resizeDirection = direction
      this.planResizeState.startPosition = { x: event.clientX, y: event.clientY }
      this.planResizeState.originalPlan = { ...plan }
      this.planResizeState.currentPlan = { ...plan }

      // 添加全局事件监听器
      document.addEventListener('mousemove', this.handleResizeMove)
      document.addEventListener('mouseup', this.handleResizeEnd)

      console.log('开始调整计划大小:', plan.planId, direction)
    },

    /**
     * 处理调整大小移动
     * @param {MouseEvent} event - 鼠标事件
     */
    handleResizeMove(event) {
      if (!this.planResizeState.isResizing) return

      // 计算鼠标移动距离
      const deltaX = event.clientX - this.planResizeState.startPosition.x
      const deltaY = event.clientY - this.planResizeState.startPosition.y

      // 根据调整方向计算新的计划范围
      const newPlan = this.calculateNewPlanBounds(deltaX, deltaY)

      if (newPlan) {
        this.planResizeState.currentPlan = newPlan

        // 实时更新计划显示
        this.updatePlanDisplay(newPlan)

        // 添加拖拽时的视觉反馈
        this.addResizeFeedback(newPlan)
      }
    },

    /**
     * 结束调整大小
     */
    handleResizeEnd() {
      if (!this.planResizeState.isResizing) return

      const planId = this.planResizeState.resizingPlanId
      const newPlan = this.planResizeState.currentPlan

      // 移除全局事件监听器
      document.removeEventListener('mousemove', this.handleResizeMove)
      document.removeEventListener('mouseup', this.handleResizeEnd)

      // 重置调整状态
      this.planResizeState.isResizing = false
      this.planResizeState.resizingPlanId = null
      this.planResizeState.resizeDirection = null

      console.log('结束调整计划大小:', planId, newPlan)

      // 发射事件给父组件，通知计划已更新
      this.$emit('plan-resized', {
        yardId: this.yardData?.yardId,
        planId: planId,
        originalPlan: this.planResizeState.originalPlan,
        newPlan: newPlan
      })

      // 清空临时状态
      this.planResizeState.originalPlan = null
      this.planResizeState.currentPlan = null
    },

    /**
     * 计算新的计划边界
     * @param {number} deltaX - X方向移动距离
     * @param {number} deltaY - Y方向移动距离
     * @returns {Object|null} 新的计划对象
     */
    calculateNewPlanBounds(deltaX, deltaY) {
      const originalPlan = this.planResizeState.originalPlan
      const direction = this.planResizeState.resizeDirection

      if (!originalPlan || !direction) return null

      // 将像素移动距离转换为贝排坐标
      const bayDelta = Math.round(deltaX / this.config.cellWidth)
      const rowDelta = Math.round(deltaY / this.config.cellHeight)

      // 计算当前计划的实际边界（考虑方向）
      const currentMinBay = Math.min(originalPlan.startBay, originalPlan.endBay)
      const currentMaxBay = Math.max(originalPlan.startBay, originalPlan.endBay)
      const currentMinRow = Math.min(originalPlan.startRow, originalPlan.endRow)
      const currentMaxRow = Math.max(originalPlan.startRow, originalPlan.endRow)

      let newMinBay = currentMinBay
      let newMaxBay = currentMaxBay
      let newMinRow = currentMinRow
      let newMaxRow = currentMaxRow

      // 根据调整方向更新边界
      switch (direction) {
        case 'n': // 上边 - 调整最小排号
          newMinRow = Math.max(1, currentMinRow + rowDelta)
          break
        case 'e': // 右边 - 调整最大贝号
          newMaxBay = Math.min(this.yardData.bayCount * 2 - 1, currentMaxBay + bayDelta)
          break
        case 's': // 下边 - 调整最大排号
          newMaxRow = Math.min(this.yardData.rowCount, currentMaxRow + rowDelta)
          break
        case 'w': // 左边 - 调整最小贝号
          newMinBay = Math.max(1, currentMinBay + bayDelta)
          break
      }

      // 确保贝号是奇数
      if (newMinBay % 2 === 0) newMinBay -= 1
      if (newMaxBay % 2 === 0) newMaxBay += 1

      // 确保范围有效
      if (newMaxBay - newMinBay < 1 || newMaxRow - newMinRow < 0) {
        return null
      }

      // 根据原始计划的方向，确定新的startBay和endBay
      let newStartBay, newEndBay, newStartRow, newEndRow

      if (originalPlan.startBay <= originalPlan.endBay) {
        // 原始计划是从小到大：startBay <= endBay
        newStartBay = newMinBay
        newEndBay = newMaxBay
      } else {
        // 原始计划是从大到小：startBay > endBay
        newStartBay = newMaxBay
        newEndBay = newMinBay
      }

      if (originalPlan.startRow <= originalPlan.endRow) {
        // 原始计划是从小到大：startRow <= endRow
        newStartRow = newMinRow
        newEndRow = newMaxRow
      } else {
        // 原始计划是从大到小：startRow > endRow
        newStartRow = newMaxRow
        newEndRow = newMinRow
      }

      return {
        ...originalPlan,
        startBay: newStartBay,
        endBay: newEndBay,
        startRow: newStartRow,
        endRow: newEndRow
      }
    },

    /**
     * 更新计划显示
     * @param {Object} plan - 计划对象
     */
    updatePlanDisplay(plan) {
      // 找到对应的计划并更新
      const planIndex = this.yardPlans.findIndex(p => p.planId === plan.planId)
      if (planIndex !== -1) {
        // 临时更新显示，但不修改原始数据
        this.$set(this.yardPlans, planIndex, { ...plan })
      }
    },

    /**
     * 添加拖拽调整时的视觉反馈
     * @param {Object} plan - 计划对象
     */
    addResizeFeedback(plan) {
      // 可以在这里添加一些视觉反馈，比如显示调整后的尺寸信息
      // 暂时通过控制台输出
      const minBay = Math.min(plan.startBay, plan.endBay)
      const maxBay = Math.max(plan.startBay, plan.endBay)
      const minRow = Math.min(plan.startRow, plan.endRow)
      const maxRow = Math.max(plan.startRow, plan.endRow)

      const bayCount = Math.floor((maxBay - minBay) / 2) + 1
      const rowCount = maxRow - minRow + 1
      console.log(`调整中: ${bayCount}贝 × ${rowCount}排`)
    },

    /**
     * 处理集装箱覆盖层鼠标进入事件
     * @param {Object} container - 集装箱对象
     */
    onContainerOverlayHover(container) {
      this.hoveredContainerId = `${container.bay}-${container.row}-${container.tier || 1}`
    },
    /**
     * 处理集装箱覆盖层鼠标离开事件
     */
    onContainerOverlayLeave() {
      this.hoveredContainerId = null
    },
    /**
     * 处理集装箱覆盖层点击事件
     * @param {Object} container - 集装箱对象
     */
    onContainerOverlayClick(container) {
      const containerId = `${container.bay}-${container.row}-${container.tier || 1}`

      // 切换选中状态
      if (this.selectedContainerId === containerId) {
        this.selectedContainerId = null
      } else {
        this.selectedContainerId = containerId
      }

      // 发射事件给父组件
      this.$emit('container-selected', {
        yardId: this.yardData?.yardId,
        container: container,
        isSelected: this.selectedContainerId === containerId
      })
    },

    /**
     * 获取指定位置的所有集装箱
     * @param {number} bay - 贝号
     * @param {number} row - 排号
     * @returns {Array} 该位置的所有集装箱数组
     */
    getContainersAtPosition(bay, row) {
      if (!this.yardData) return []
      return this.containerData
          .filter(container => container.bay === bay && container.row === row)
          .sort((a, b) => (a.tier || 1) - (b.tier || 1))
    },

    /**
     * 获取指定位置指定层级的集装箱
     * @param {number} bay - 贝号
     * @param {number} row - 排号
     * @param {number} tier - 层级
     * @returns {Object|null} 该层级的集装箱对象或null
     */
    getContainerAtTier(bay, row, tier) {
      return this.containerData.find(container =>
          container.bay === bay &&
          container.row === row &&
          (container.tier || 1) === tier
      ) || null
    },
    /**
     * 获取集装箱的悬浮提示信息
     * @param {Object} container - 集装箱对象
     * @returns {string} 提示信息
     */
    getContainerTooltip(container) {
      if (!container) return '无集装箱信息'
      return `集装箱: ${container.containerNo}\n位置: ${container.bay}贝${container.row}排${container.tier}层`
    },
    /**
     * 获取堆场计划的悬浮提示信息
     * @param {Object} plan - 计划对象
     * @returns {string} 提示信息
     */
    getPlanTooltip(plan) {
      const description = plan.description || '无描述'
      const createdAt = plan.createdAt ? new Date(plan.createdAt).toLocaleString() : '未知时间'

      // 正确计算计划区域大小，处理startBay可能大于endBay的情况
      const minBay = Math.min(plan.startBay, plan.endBay)
      const maxBay = Math.max(plan.startBay, plan.endBay)
      const minRow = Math.min(plan.startRow, plan.endRow)
      const maxRow = Math.max(plan.startRow, plan.endRow)

      // 计算贝数和排数（确保为奇数贝号）
      const bayCount = Math.floor((maxBay - minBay) / 2) + 1
      const rowCount = maxRow - minRow + 1
      const areaSize = `${bayCount}贝 × ${rowCount}排`

      return `计划ID: ${plan.planId}\n区域: ${plan.startBay}-${plan.endBay}贝 ${plan.startRow}-${plan.endRow}排\n大小: ${areaSize}\n创建时间: ${createdAt}\n描述: ${description}`
    },
    /**
     * 获取堆场加锁的悬浮提示信息
     * @param {Object} lock - 加锁对象
     * @returns {string} 提示信息
     */
    getLockTooltip(lock) {
      const description = lock.description || '无描述'
      const lockTime = lock.lockTime ? new Date(lock.lockTime).toLocaleString() : '未知时间'
      const lockOperator = lock.lockOperator || '未知操作员'
      const lockReason = lock.lockReason || '未知原因'

      return `加锁ID: ${lock.lockId}\n位置: ${lock.bay}贝${lock.row}排\n加锁时间: ${lockTime}\n操作员: ${lockOperator}\n加锁原因: ${lockReason}\n描述: ${description}`
    },

    /**
     * 将16进制颜色转换为RGB对象
     * @param {string} hexColor - 16进制颜色代码
     * @returns {Object|null} RGB对象 {r, g, b} 或 null
     */
    hexToRgb(hexColor) {
      const hex = hexColor.replace('#', '')
      if (hex.length !== 6) return null

      return {
        r: parseInt(hex.substr(0, 2), 16),
        g: parseInt(hex.substr(2, 2), 16),
        b: parseInt(hex.substr(4, 2), 16)
      }
    },
    /**
     * 调整颜色亮度（变暗或变亮）
     * @param {string} hexColor - 16进制颜色代码
     * @param {number} amount - 调整程度 (0-1)，正数变亮，负数变暗
     * @returns {string} 调整后的16进制颜色代码
     */
    adjustColorBrightness(hexColor, amount) {
      const rgb = this.hexToRgb(hexColor)
      if (!rgb) return hexColor

      const { r, g, b } = rgb
      let newR, newG, newB

      if (amount > 0) {
        // 变亮
        newR = Math.min(255, Math.floor(r + (255 - r) * amount))
        newG = Math.min(255, Math.floor(g + (255 - g) * amount))
        newB = Math.min(255, Math.floor(b + (255 - b) * amount))
      } else {
        // 变暗
        const factor = 1 + amount // amount为负数
        newR = Math.floor(r * factor)
        newG = Math.floor(g * factor)
        newB = Math.floor(b * factor)
      }

      const toHex = (n) => n.toString(16).padStart(2, '0')
      return `#${toHex(newR)}${toHex(newG)}${toHex(newB)}`
    },

    /**
     * 获取指定位置集装箱的层级数量
     * @param {Object} container - 集装箱对象
     * @returns {number} 该位置的集装箱层级数量
     */
    getContainerTierCount(container) {
      const containersAtPosition = this.getContainersAtPosition(container.bay, container.row)
      return containersAtPosition.length
    },
    /**
     * 获取指定层级的堆叠位置
     * @param {number} tier - 层级（从1开始）
     * @returns {string} CSS transform字符串
     */
    getStackPosition(tier) {
      const height = this.config.cellHeight - this.config.cuboidOffset
      const spacing = height + 3 // 长方体高度 + 间距
      const translateY = -(tier - 1) * spacing
      return `translate(-50%, -50%) translateY(${translateY}px)`
    },
    /**
     * 根据集装箱类型获取面样式（优化版本）
     * @param {Object} container - 集装箱对象
     * @returns {Object} 面样式对象
     */
    getFaceStyles(container) {
      // 使用缓存来避免重复计算
      const cacheKey = `${container.bay}-${container.row}-${container.tier || 1}-${container.color || 'default'}`
      if (this._faceStylesCache && this._faceStylesCache[cacheKey]) {
        return this._faceStylesCache[cacheKey]
      }

      // 动态计算配置
      const baseWidth = this.config.cellWidth - this.config.cuboidOffset
      const baseHeight = this.config.cellHeight - this.config.cuboidOffset
      const width = container.bay % 2 === 0 ? baseWidth * 2 : baseWidth
      const height = baseHeight
      const depth = height // 深度等于高度

      // 基础样式
      const baseStyles = {
        front: {
          width: `${width}px`,
          height: `${height}px`,
          transform: `translateZ(${depth/2}px)`
        },
        back: {
          width: `${width}px`,
          height: `${height}px`,
          transform: `translateZ(-${depth/2}px) rotateY(180deg)`
        },
        right: {
          width: `${depth}px`,
          height: `${height}px`,
          transform: `rotateY(90deg) translateZ(${width - depth/2}px)`
        },
        left: {
          width: `${depth}px`,
          height: `${height}px`,
          transform: `rotateY(-90deg) translateZ(${depth/2}px)`
        },
        top: {
          width: `${width}px`,
          height: `${depth}px`,
          transform: `rotateX(90deg) translateZ(${depth/2}px)`
        },
        bottom: {
          width: `${width}px`,
          height: `${depth}px`,
          transform: `rotateX(-90deg) translateZ(${depth/2}px)`
        }
      }

      // 如果集装箱有颜色，为所有面应用动态颜色
      if (container.color) {
        const color = container.color
        const rgb = this.hexToRgb(color)

        // 为每个面应用动态颜色，不同面使用不同的亮度
        Object.keys(baseStyles).forEach(faceKey => {
          let faceColor = color

          // 根据面的类型调整颜色亮度
          switch(faceKey) {
            case 'front':
              faceColor = color // 正面使用原色
              break
            case 'back':
              faceColor = this.adjustColorBrightness(color, -0.1) // 背面稍暗
              break
            case 'right':
            case 'left':
              faceColor = this.adjustColorBrightness(color, -0.2) // 侧面更暗
              break
            case 'top':
              faceColor = this.adjustColorBrightness(color, 0.1) // 顶面稍亮
              break
            case 'bottom':
              faceColor = this.adjustColorBrightness(color, -0.3) // 底面最暗
              break
          }

          baseStyles[faceKey].background = faceColor

          // 添加动态阴影效果（仅在渲染完成后添加，避免加载时卡顿）
          if (rgb && this.loadingState.isFullyLoaded) {
            baseStyles[faceKey].boxShadow = `0 0 5px rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.3)`
          }
        })
      }

      // 缓存结果
      if (!this._faceStylesCache) {
        this._faceStylesCache = {}
      }
      this._faceStylesCache[cacheKey] = baseStyles

      return baseStyles
    },
  }
}
</script>

<style scoped>

.control-group label {
  margin-bottom: 8px;
  font-weight: bold;
  color: #333;
}

.control-group input[type="range"] {
  width: 100%;
  margin: 5px 0;
}

.stack {
  position: relative;
  transform-style: preserve-3d;
  transform: perspective(2000px) rotateX(-20deg) rotateY(0deg);
  transition: transform 0.3s ease;
}

.stack:hover {
  transform: perspective(2000px) rotateX(-40deg) rotateY(-15deg);
}

.cuboid {
  position: absolute;
  transform-style: preserve-3d;
}

.face {
  position: absolute;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 24px;
  font-weight: bold;
  color: white;
  text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
  border: 2px solid #B8860B;
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
}

/* 面的基础样式，尺寸和transform由JavaScript动态设置 */
.front { background: #FFD700; }
.back { background: #DAA520; }
.right { background: #B8860B; }
.left { background: #B8860B; }
.top { background: #FFA500; }
.bottom { background: #CD853F; }
/* 通用样式变量和基础设置 */
* {
  box-sizing: border-box !important;
}

/* 操作模式切换Tab样式 */
.operation-tabs {
  margin-bottom: 8px;
  display: flex;
  justify-content: flex-start;
  width: 100%;
}

.tab-list {
  display: flex;
  gap: 2px;
  background: #f8f9fa;
  border-radius: 6px;
  padding: 2px;
  border: 1px solid #e9ecef;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
}

.tab-item {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 6px 12px;
  border: none;
  border-radius: 4px;
  background: transparent;
  color: #6c757d;
  cursor: pointer;
  transition: all 0.2s ease;
  position: relative;
  font-size: 12px;
  font-weight: 500;
  min-width: 50px;
}

.tab-item:hover {
  background: #e9ecef;
  color: #495057;
}

.tab-item.active {
  background: #007bff;
  color: white;
  box-shadow: 0 2px 6px rgba(0, 123, 255, 0.3);
}

.tab-item.active:hover {
  background: #0056b3;
}

.tab-text {
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.3px;
}

.yard-view {
  font-family: 'Courier New', monospace;
  position: relative;
  z-index: 1;
  display: block; /* 改为block布局 */
  width: 100%;
  height: 100%;
  overflow: auto; /* 保持滚动条 */
  padding: 10px; /* 添加一些内边距 */
}

/* 堆场网格样式 */
.yard-grid {
  border: 1px solid #333;
  background: #fff;
  width: fit-content; /* 保持内容宽度，不压缩 */
  min-width: fit-content; /* 确保最小宽度不被压缩 */
  user-select: none;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  position: relative;
  z-index: 1;
  transform-style: preserve-3d;
  backface-visibility: hidden;
  overflow: visible;
  margin: 0; /* 确保没有外边距 */
  float: left; /* 强制左对齐 */
}

/* 查看模式样式 */
.yard-grid.view-mode {
  pointer-events: auto;
}

.yard-grid.view-mode .grid-cell {
  cursor: pointer;
}

.yard-grid.view-mode .grid-cell:hover {
  background-color: rgba(0, 123, 255, 0.1) !important;
  border-color: rgba(0, 123, 255, 0.3) !important;
  transform: scale(1.05);
  z-index: 10;
}

/* 计划模式样式 */
.yard-grid.plan-mode {
  pointer-events: auto;
}

.yard-grid.plan-mode .grid-cell {
  cursor: pointer;
}

.yard-grid.plan-mode .grid-cell:hover {
  background-color: rgba(76, 175, 80, 0.1) !important;
  border-color: rgba(76, 175, 80, 0.3) !important;
  transform: scale(1.05);
  z-index: 10;
}

/* 加锁模式样式 */
.yard-grid.lock-mode {
  pointer-events: auto;
}

.yard-grid.lock-mode .grid-cell {
  cursor: pointer;
}

.yard-grid.lock-mode .grid-cell:hover {
  background-color: rgba(255, 193, 7, 0.1) !important;
  border-color: rgba(255, 193, 7, 0.3) !important;
  transform: scale(1.05);
  z-index: 10;
}

.column-headers {
  display: flex;
  border-top: 2px solid #333;
  transform-style: preserve-3d;
  backface-visibility: hidden;
}

/* 通用头部样式 */
.bay-header,
.column-header {
  background: #f5f5f5;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: bold;
  padding: 0; /* 移除padding，让JavaScript设置的宽高生效 */
  box-sizing: border-box; /* 确保边框和内边距包含在总尺寸内 */
}

.bay-header {
  border-right: 2px solid #333;
}

.column-header {
  border-right: 1px solid #ccc;
}

.column-header:last-child {
  border-right: none;
}

.column-header.empty {
  background-color: #f9f9f9;
}

/* 贝号悬浮样式 */
.column-header.bay-hovered {
  background: linear-gradient(135deg, #007bff 0%, #0056b3 100%) !important;
  color: white !important;
  box-shadow: 0 4px 12px rgba(0, 123, 255, 0.4);
  z-index: 10;
}

.grid-cell.bay-hovered {
  background-color: rgba(25, 118, 210, 0.1) !important;
  border-color: rgba(25, 118, 210, 0.3) !important;
  transition: all 0.2s ease;
}

/* 行样式 */
.yard-row {
  display: flex;
  border-bottom: 1px solid #ccc;
  transform-style: preserve-3d;
  backface-visibility: hidden;
}

.yard-row:last-child {
  border-bottom: 2px solid #333;
}

/* 贝号样式 */
.bay-number {
  border-right: 2px solid #333;
  background: #f5f5f5;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: bold;
  padding: 0; /* 移除padding，让JavaScript设置的宽高生效 */
  font-size: 12px;
  box-sizing: border-box; /* 确保边框和内边距包含在总尺寸内 */
}

.bay-number.empty {
  background-color: #f9f9f9;
}

/* 网格单元格样式 */
.grid-cell {
  border-right: 1px solid #eee;
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  cursor: pointer;
  overflow: hidden;
  transform-origin: center center;
  background-image:
      radial-gradient(circle at 20% 20%, rgba(255, 255, 255, 0.1) 1px, transparent 1px),
      radial-gradient(circle at 80% 80%, rgba(0, 0, 0, 0.05) 1px, transparent 1px);
  background-size: 8px 8px, 12px 12px;
}

/* 贝位区域悬停效果 */
.grid-cell:hover {
  background-color: rgba(0, 123, 255, 0.1) !important;
  border-color: rgba(0, 123, 255, 0.3) !important;
  transform: scale(1.05);
  z-index: 10;
}

.grid-cell:last-child {
  border-right: none;
}

/* 集装箱覆盖层样式 - 只保留基本定位 */
.container-overlay {
  position: absolute;
  background: transparent;
  border: none;
  z-index: 3;
  /* 硬件加速 */
  will-change: transform;
  transform: translateZ(0);
}

/* 3D集装箱包装器 */
.container-3d-wrapper {
  position: relative;
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
}


/* 3D堆叠容器 */
.container-3d-wrapper .stack {
  position: relative;
  transform-style: preserve-3d;
  transform: perspective(1000px) rotateX(305deg) rotateY(0deg) translateZ(0);
  transition: transform 0.3s ease;
  transform-origin: center center; /* 确保变换以中心为基准 */
  /* 硬件加速 */
  will-change: transform;
  backface-visibility: hidden;
}

/* 3D长方体 */
.container-3d-wrapper .cuboid {
  position: absolute;
  transform-style: preserve-3d;
  left: 50%;
  top: 50%;
  transform-origin: center center; /* 确保长方体以中心为基准定位 */
  /* 硬件加速 */
  will-change: transform;
  backface-visibility: hidden;
  transform: translateZ(0);
}

/* 3D长方体面 - 性能优化版本 */
.container-3d-wrapper .face {
  position: absolute;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
  font-weight: bold;
  color: white;
  text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5);
  border: 1px solid #000000;
  /* 硬件加速和性能优化 */
  will-change: transform;
  backface-visibility: hidden;
  transform: translateZ(0);
  /* 减少重绘和重排 */
  contain: layout style paint;
  /* 优化渲染性能 */
  transform-style: preserve-3d;
  /* 延迟阴影渲染，避免加载时卡顿 */
  box-shadow: 0 0 3px rgba(0, 0, 0, 0.1);
  transition: box-shadow 0.3s ease;
}


/* 3D长方体悬停效果 */
.container-overlay.container-hovered .container-3d-wrapper .stack {
  transform: perspective(1000px) rotateX(340deg) rotateY(0deg) translateZ(0);
}


/* 集装箱覆盖层选中状态 */
.container-overlay.container-selected {
  animation: containerPulse 1s cubic-bezier(0.4, 0, 0.6, 1) infinite;
  border: 3px solid rgba(0, 123, 255, 0.4);
  border-radius: 6px;
  z-index: 15;
  will-change: transform, opacity;
  transform: translateZ(0);
  backface-visibility: hidden;
  box-shadow:
      0 6px 20px rgba(0, 123, 255, 0.5),
      0 0 0 1px rgba(255, 255, 255, 0.3);
}

.container-overlay.container-selected::before {
  content: '';
  position: absolute;
  top: -4px;
  left: -4px;
  right: -4px;
  bottom: -4px;
  border: 2px solid rgba(0, 123, 255, 0.4);
  border-radius: 6px;
  opacity: 0.6;
  animation: containerPulseRing 1s cubic-bezier(0.4, 0, 0.6, 1) infinite;
  pointer-events: none;
  will-change: transform, opacity;
  transform: translateZ(0);
  backface-visibility: hidden;
}

/* 集装箱脉冲动画关键帧 */
@keyframes containerPulse {
  0% {
    transform: scale(1);
    opacity: 1;
  }
  50% {
    transform: scale(1.01);
    opacity: 0.98;
  }
  100% {
    transform: scale(1);
    opacity: 1;
  }
}

@keyframes containerPulseRing {
  0% {
    transform: scale(1);
    opacity: 0.5;
  }
  60% {
    transform: scale(1.06);
    opacity: 0.15;
  }
  100% {
    transform: scale(1.12);
    opacity: 0;
  }
}

/* 计划覆盖层基础样式 */
.plan-overlay {
  position: absolute;
  border: none;
  border-radius: 4px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
  display: flex;
  align-items: center;
  justify-content: center;
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  cursor: pointer;
  pointer-events: auto;
  z-index: 2;
}

/* 加锁覆盖层基础样式 */
.lock-overlay {
  position: absolute;
  border: 2px solid rgba(255, 152, 0, 0.6);
  border-radius: 4px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
  display: flex;
  align-items: center;
  justify-content: center;
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  cursor: pointer;
  pointer-events: auto;
  z-index: 2;
}

.lock-overlay .lock-icon {
  font-size: 24px;
  color: rgba(255, 152, 0, 0.8);
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
  animation: lockPulse 2s ease-in-out infinite;
}



/* 计划覆盖层悬停效果 */
.plan-overlay.plan-hovered {
  transform: scale(1.05) translateY(-2px);
  z-index: 12;
  filter: brightness(1.1) saturate(1.2);
  border-width: 3px;
}

/* 加锁覆盖层悬停效果 */
.lock-overlay.lock-hovered {
  transform: scale(1.05) translateY(-2px);
  z-index: 12;
  filter: brightness(1.1) saturate(1.2);
  border-width: 3px;
}


/* 计划覆盖层选中状态 */
.plan-overlay.plan-selected {
  animation: planPulse 1s cubic-bezier(0.4, 0, 0.6, 1) infinite;
  border-width: 3px;
  z-index: 14;
  will-change: transform, opacity;
  transform: translateZ(0);
  backface-visibility: hidden;
}

/* 加锁覆盖层选中状态 */
.lock-overlay.lock-selected {
  animation: planPulse 1s cubic-bezier(0.4, 0, 0.6, 1) infinite;
  border-width: 3px;
  z-index: 14;
  will-change: transform, opacity;
  transform: translateZ(0);
  backface-visibility: hidden;
}

.lock-overlay.lock-selected::before {
  content: '';
  position: absolute;
  top: -4px;
  left: -4px;
  right: -4px;
  bottom: -4px;
  border: 2px solid #FF9800;
  border-radius: 6px;
  opacity: 0.6;
  animation: planPulseRing 1s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}


/* 计划拖拽控制点容器 */
.plan-resize-handles {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  pointer-events: none;
  z-index: 15;
}

/* 拖拽控制点基础样式 */
.resize-handle {
  position: absolute;
  background: #007bff;
  border: 2px solid white;
  cursor: pointer;
  pointer-events: auto;
  z-index: 16;
  transition: all 0.2s ease;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
}

.resize-handle:hover {
  background: #0056b3;
  box-shadow: 0 4px 12px rgba(0, 123, 255, 0.5);
}

.resize-handle-n:hover {
  transform: translateX(-50%) scale(1.1);
}

.resize-handle-e:hover {
  transform: translateY(-50%) scale(1.1);
}

.resize-handle-s:hover {
  transform: translateX(-50%) scale(1.1);
}

.resize-handle-w:hover {
  transform: translateY(-50%) scale(1.1);
}

.resize-handle:active {
  background: #004085;
}

.resize-handle-n:active {
  transform: translateX(-50%) scale(1.05);
}

.resize-handle-e:active {
  transform: translateY(-50%) scale(1.05);
}

.resize-handle-s:active {
  transform: translateX(-50%) scale(1.05);
}

.resize-handle-w:active {
  transform: translateY(-50%) scale(1.05);
}

/* 上边控制点 */
.resize-handle-n {
  top: -6px;
  left: 50%;
  transform: translateX(-50%);
  width: 20px;
  height: 12px;
  border-radius: 6px;
  cursor: n-resize;
}

/* 右边控制点 */
.resize-handle-e {
  top: 50%;
  right: -6px;
  transform: translateY(-50%);
  width: 12px;
  height: 20px;
  border-radius: 6px;
  cursor: e-resize;
}

/* 下边控制点 */
.resize-handle-s {
  bottom: -6px;
  left: 50%;
  transform: translateX(-50%);
  width: 20px;
  height: 12px;
  border-radius: 6px;
  cursor: s-resize;
}

/* 左边控制点 */
.resize-handle-w {
  top: 50%;
  left: -6px;
  transform: translateY(-50%);
  width: 12px;
  height: 20px;
  border-radius: 6px;
  cursor: w-resize;
}

/* 拖拽调整时的计划覆盖层样式 */
.plan-overlay.resizing {
  opacity: 0.8;
  border-style: dashed;
  animation: none;
}

/* 拖拽调整时的控制点样式 */
.plan-resize-handles.resizing .resize-handle {
  background: #28a745;
  box-shadow: 0 0 15px rgba(40, 167, 69, 0.6);
}

.plan-resize-handles.resizing .resize-handle-n {
  transform: translateX(-50%) scale(1.3);
}

.plan-resize-handles.resizing .resize-handle-e {
  transform: translateY(-50%) scale(1.3);
}

.plan-resize-handles.resizing .resize-handle-s {
  transform: translateX(-50%) scale(1.3);
}

.plan-resize-handles.resizing .resize-handle-w {
  transform: translateY(-50%) scale(1.3);
}

/* 拖拽调整时的控制点动画 */
.plan-resize-handles.resizing .resize-handle::after {
  content: '';
  position: absolute;
  top: -3px;
  left: -3px;
  right: -3px;
  bottom: -3px;
  border: 2px solid rgba(40, 167, 69, 0.3);
  border-radius: 50%;
  animation: resizePulse 1s infinite;
}

@keyframes resizePulse {
  0% {
    transform: scale(1);
    opacity: 0.7;
  }
  50% {
    transform: scale(1.2);
    opacity: 0.3;
  }
  100% {
    transform: scale(1.4);
    opacity: 0;
  }
}

.plan-overlay.plan-selected::before {
  content: '';
  position: absolute;
  top: -4px;
  left: -4px;
  right: -4px;
  bottom: -4px;
  border: 2px solid #007bff;
  border-radius: 6px;
  opacity: 0.6;
  animation: planPulseRing 1s cubic-bezier(0.4, 0, 0.6, 1) infinite;
  pointer-events: none;
  will-change: transform, opacity;
  transform: translateZ(0);
  backface-visibility: hidden;
}



/* 脉冲动画关键帧 */
@keyframes planPulse {
  0% {
    transform: scale(1);
    opacity: 1;
  }
  50% {
    transform: scale(1.01);
    opacity: 0.98;
  }
  100% {
    transform: scale(1);
    opacity: 1;
  }
}

@keyframes planPulseRing {
  0% {
    transform: scale(1);
    opacity: 0.5;
  }
  60% {
    transform: scale(1.06);
    opacity: 0.15;
  }
  100% {
    transform: scale(1.12);
    opacity: 0;
  }
}

/* 选中单元格样式 - 完全复制计划覆盖层样式 */
.grid-cell.selected {
  background: rgba(76, 175, 80, 0.05) repeating-linear-gradient(
      45deg,
      transparent,
      transparent 6px,rgba(76, 175, 80, 0.2) 6px,rgba(76, 175, 80, 0.2) 8px
  ) !important;
  background-size: 8px 8px !important;
  border: none !important;
  border-radius: 4px;
  position: relative;
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  z-index: 5;
}

.grid-cell.selected::after {
  content: '';
  position: absolute;
  top: -2px;
  left: -2px;
  right: -2px;
  bottom: -2px;
  background: linear-gradient(135deg,
  rgba(76, 175, 80, 0.1) 0%,
  rgba(76, 175, 80, 0.05) 50%,
  rgba(76, 175, 80, 0.1) 100%);
  border-radius: 4px;
  pointer-events: none;
  transition: all 0.3s ease;
  animation: selectedPulse 2s ease-in-out infinite;
}

@keyframes selectedPulse {
  0%, 100% {
    opacity: 0.6;
    transform: scale(1);
  }
  50% {
    opacity: 0.8;
    transform: scale(1.01);
  }
}

/* 加锁单元格样式 - 使用锁图标，与计划样式区分 */
.grid-cell.locked {
  background: rgba(255, 152, 0, 0.1) !important;
  border: 2px solid rgba(255, 152, 0, 0.6) !important;
  border-radius: 4px;
  position: relative;
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  z-index: 5;
}

.grid-cell.locked::before {
  content: '🔒';
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  font-size: 16px;
  color: #FF9800;
  text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.3);
  pointer-events: none;
  z-index: 6;
  animation: lockPulse 2s ease-in-out infinite;
}

.grid-cell.locked::after {
  content: '';
  position: absolute;
  top: -2px;
  left: -2px;
  right: -2px;
  bottom: -2px;
  background: linear-gradient(135deg,
  rgba(255, 152, 0, 0.1) 0%,
  rgba(255, 152, 0, 0.05) 50%,
  rgba(255, 152, 0, 0.1) 100%);
  border-radius: 4px;
  pointer-events: none;
  transition: all 0.3s ease;
  animation: lockedPulse 2s ease-in-out infinite;
}

@keyframes lockedPulse {
  0%, 100% {
    opacity: 0.6;
    transform: scale(1);
  }
  50% {
    opacity: 0.8;
    transform: scale(1.01);
  }
}


/* 模态框遮罩层 */
.yard-detail-overlay {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(0, 0, 0, 0.6);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 2000;
  animation: fadeIn 0.3s ease-out;
}

/* 模态框主体 */
.yard-detail-modal {
  background: white;
  border-radius: 8px;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
  max-width: 90vw;
  max-height: 90vh;
  width: auto;
  height: auto;
  min-width: 600px;
  min-height: 400px;
  display: flex;
  flex-direction: column;
  animation: slideIn 0.3s ease-out;
  overflow: hidden;
}

/* 模态框头部 */
.modal-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 12px 16px;
  border-bottom: 1px solid #e9ecef;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: white;
  flex-shrink: 0;
}

.header-content {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.modal-title {
  margin: 0;
  font-size: 18px;
  font-weight: 600;
}

.close-btn {
  background: none;
  border: none;
  color: white;
  font-size: 24px;
  cursor: pointer;
  padding: 4px;
  border-radius: 4px;
  transition: background-color 0.2s ease;
}

.close-btn:hover {
  background: rgba(255, 255, 255, 0.2);
}

.close-icon {
  display: block;
  line-height: 1;
}

/* 模态框内容 */
.modal-content {
  flex: 1;
  overflow: auto; /* 保持滚动条 */
  padding: 0 16px 16px 16px;
  display: flex;
  justify-content: center;
  align-items: flex-start; /* 改为flex-start，避免强制居中 */
  min-height: 0;
  position: relative; /* 为遮罩层提供定位基准 */
}

/* 模态框底部 */
.modal-footer {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  padding: 8px 12px;
  border-top: 1px solid #e9ecef;
  background: #f8f9fa;
  flex-shrink: 0;
}

.btn {
  padding: 6px 12px;
  border: none;
  border-radius: 4px;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.2s ease;
  font-size: 12px;
}

.btn-secondary {
  background: #6c757d;
  color: white;
}

.btn-secondary:hover {
  background: #5a6268;
}

.btn-primary {
  background: #3498db;
  color: white;
}

.btn-primary:hover {
  background: #2980b9;
}

.btn-warning {
  background: #f39c12;
  color: white;
}

.btn-warning:hover {
  background: #e67e22;
}

.btn-success {
  background: #27ae60;
  color: white;
}

.btn-success:hover {
  background: #229954;
}

/* 动画效果 */
@keyframes fadeIn {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

@keyframes slideIn {
  from {
    opacity: 0;
    transform: scale(0.9) translateY(-20px);
  }
  to {
    opacity: 1;
    transform: scale(1) translateY(0);
  }
}

/* 响应式设计 */
@media (max-width: 768px) {
  .yard-detail-modal {
    max-width: 95vw;
    max-height: 95vh;
    min-width: 400px;
    min-height: 300px;
  }

  .modal-header {
    padding: 8px 12px;
  }

  .modal-title {
    font-size: 16px;
  }

  .modal-content {
    padding: 4px;
  }

  .modal-footer {
    padding: 6px 8px;
    flex-direction: row;
    flex-wrap: wrap;
  }

  .btn {
    padding: 4px 8px;
    font-size: 11px;
  }

  /* 移除堆场图的响应式压缩，保持原始尺寸 */
  .bay-number,
  .bay-header {
    font-size: 10px;
  }

  /* Tab响应式设计 */
  .tab-item {
    padding: 4px 8px;
    font-size: 11px;
    min-width: 40px;
  }

  .tab-text {
    font-size: 11px;
  }
}

/* 选择信息面板样式 */
.selection-info-panel {
  position: fixed;
  top: 20px;
  left: 20px;
  background: white;
  border: 2px solid #007bff;
  border-radius: 8px;
  padding: 16px;
  box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
  min-width: 220px;
  max-width: 300px;
  z-index: 1000;
  backdrop-filter: blur(10px);
  animation: panelSlideIn 0.3s ease-out;
}

/* 加锁信息面板样式 */
.lock-info-panel {
  position: fixed;
  top: 20px;
  left: 20px;
  background: white;
  border: 2px solid #FF9800;
  border-radius: 8px;
  padding: 16px;
  box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
  min-width: 220px;
  max-width: 300px;
  z-index: 1000;
  backdrop-filter: blur(10px);
  animation: panelSlideIn 0.3s ease-out;
}

.selection-info-panel h4 {
  margin: 0 0 12px 0;
  color: #007bff;
  font-size: 16px;
  font-weight: 600;
  border-bottom: 1px solid #e9ecef;
  padding-bottom: 8px;
}

.lock-info-panel h4 {
  margin: 0 0 12px 0;
  color: #FF9800;
  font-size: 16px;
  font-weight: 600;
  border-bottom: 1px solid #e9ecef;
  padding-bottom: 8px;
}

.info-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 8px;
  font-size: 13px;
  padding: 4px 0;
}

.info-item .label {
  font-weight: 500;
  color: #495057;
  min-width: 70px;
  flex-shrink: 0;
}

.info-item .value {
  color: #007bff;
  font-weight: 600;
  text-align: right;
  flex: 1;
  margin-left: 8px;
}

@keyframes panelSlideIn {
  from {
    opacity: 0;
    transform: translateX(-20px);
  }
  to {
    opacity: 1;
    transform: translateX(0);
  }
}

/* 保存计划区域样式 */
.save-plan-section {
  margin-top: 16px;
  padding-top: 16px;
  border-top: 1px solid #e9ecef;
  background: linear-gradient(135deg, rgba(40, 167, 69, 0.05) 0%, rgba(40, 167, 69, 0.02) 100%);
  border-radius: 6px;
  padding: 12px;
  margin: 16px -4px 0 -4px;
}

/* 保存加锁区域样式 */
.save-lock-section {
  margin-top: 16px;
  padding-top: 16px;
  border-top: 1px solid #e9ecef;
  background: linear-gradient(135deg, rgba(255, 152, 0, 0.05) 0%, rgba(255, 152, 0, 0.02) 100%);
  border-radius: 6px;
  padding: 12px;
  margin: 16px -4px 0 -4px;
}

.save-plan-section h5 {
  margin: 0 0 12px 0;
  color: #28a745;
  font-size: 14px;
  font-weight: 600;
  display: flex;
  align-items: center;
  gap: 6px;
}

.save-plan-section h5::before {
  content: '💾';
  font-size: 16px;
}

.save-lock-section h5 {
  margin: 0 0 12px 0;
  color: #FF9800;
  font-size: 14px;
  font-weight: 600;
  display: flex;
  align-items: center;
  gap: 6px;
}

.save-lock-section h5::before {
  content: '🔒';
  font-size: 16px;
}

.plan-description-input {
  padding: 6px 12px;
  border: 1px solid #ced4da;
  border-radius: 4px;
  font-size: 12px;
  width: 100%;
  max-width: 200px;
  transition: border-color 0.2s ease;
}

.plan-description-input:focus {
  outline: none;
  border-color: #007bff;
  box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25);
}

.lock-description-input {
  padding: 6px 12px;
  border: 1px solid #ced4da;
  border-radius: 4px;
  font-size: 12px;
  width: 100%;
  max-width: 200px;
  transition: border-color 0.2s ease;
}

.lock-description-input:focus {
  outline: none;
  border-color: #FF9800;
  box-shadow: 0 0 0 2px rgba(255, 152, 0, 0.25);
}

/* 统一按钮样式 - 与模态框底部按钮保持一致 */
.save-plan-btn {
  background: #27ae60;
  color: white;
  border: none;
  padding: 6px 12px;
  border-radius: 4px;
  font-size: 12px;
  font-weight: 500;
  cursor: pointer;
  margin-right: 8px;
  transition: all 0.2s ease;
}

.save-plan-btn:hover:not(:disabled) {
  background: #229954;
  transform: translateY(-1px);
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.save-plan-btn:disabled {
  background: #6c757d;
  cursor: not-allowed;
  transform: none;
  box-shadow: none;
}

.clear-selection-btn {
  background: #f39c12;
  color: white;
  border: none;
  padding: 6px 12px;
  border-radius: 4px;
  font-size: 12px;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.2s ease;
}

.clear-selection-btn:hover {
  background: #e67e22;
  transform: translateY(-1px);
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.save-lock-btn {
  background: #FF9800;
  color: white;
  border: none;
  padding: 6px 12px;
  border-radius: 4px;
  font-size: 12px;
  font-weight: 500;
  cursor: pointer;
  margin-right: 8px;
  transition: all 0.2s ease;
}

.save-lock-btn:hover:not(:disabled) {
  background: #F57C00;
  transform: translateY(-1px);
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.save-lock-btn:disabled {
  background: #6c757d;
  cursor: not-allowed;
  transform: none;
  box-shadow: none;
}

.clear-lock-btn {
  background: #f39c12;
  color: white;
  border: none;
  padding: 6px 12px;
  border-radius: 4px;
  font-size: 12px;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.2s ease;
}

.clear-lock-btn:hover {
  background: #e67e22;
  transform: translateY(-1px);
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

/* 按钮组样式 */
.button-group {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 12px;
}

.button-group button {
  width: 100%;
  justify-content: center;
  display: flex;
  align-items: center;
  gap: 6px;
}

.button-group button::before {
  font-size: 14px;
}

.save-plan-btn::before {
  content: '💾';
}

.clear-selection-btn::before {
  content: '🗑️';
}

.save-lock-btn::before {
  content: '🔒';
}

.clear-lock-btn::before {
  content: '🗑️';
}

/* 保存成功提示样式 */
.save-success-toast {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 2000;
  animation: toastSlideIn 0.3s ease-out;
}

/* 加锁成功提示样式 */
.lock-success-toast {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 2000;
  animation: toastSlideIn 0.3s ease-out;
}

.toast-content {
  background: #28a745;
  color: white;
  padding: 16px 20px;
  border-radius: 8px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 300px;
  max-width: 500px;
}

.lock-success-toast .toast-content {
  background: #FF9800;
}

.toast-icon {
  font-size: 20px;
  font-weight: bold;
  background: rgba(255, 255, 255, 0.2);
  border-radius: 50%;
  width: 30px;
  height: 30px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.toast-message {
  flex: 1;
  font-size: 14px;
  font-weight: 500;
  white-space: pre-line;
  line-height: 1.4;
}

.toast-close {
  background: none;
  border: none;
  color: white;
  font-size: 20px;
  font-weight: bold;
  cursor: pointer;
  padding: 0;
  width: 24px;
  height: 24px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  transition: background-color 0.2s ease;
}

.toast-close:hover {
  background: rgba(255, 255, 255, 0.2);
}

@keyframes toastSlideIn {
  from {
    opacity: 0;
    transform: translate(-50%, -60%);
  }
  to {
    opacity: 1;
    transform: translate(-50%, -50%);
  }
}

/* 智能加载状态遮罩层 */
.loading-overlay {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(255, 255, 255, 0.2);
  backdrop-filter: blur(8px);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1000;
  animation: fadeIn 0.3s ease-out;
  transition: opacity 0.5s ease-out, visibility 0.5s ease-out;
  border-radius: 8px;
}

/* 加载完成后的淡出效果 */
.loading-overlay.fade-out {
  opacity: 0;
  visibility: hidden;
}

/* 简单加载状态样式 */
.yard-loading {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 40px 40px 30px 40px;
  background: rgba(255, 255, 255, 0.98);
  border-radius: 12px;
  box-shadow:
      0 8px 32px rgba(0, 0, 0, 0.12),
      0 2px 8px rgba(0, 0, 0, 0.08);
  backdrop-filter: blur(12px);
  min-width: 280px;
  max-width: 350px;
  border: 1px solid rgba(255, 255, 255, 0.2);
  animation: slideInUp 0.4s ease-out;
}

@keyframes slideInUp {
  from {
    opacity: 0;
    transform: translateY(30px) scale(0.95);
  }
  to {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}

/* 加载标题样式 */
.loading-title {
  font-size: 18px;
  font-weight: 600;
  color: #333;
  margin-bottom: 20px;
  text-align: center;
  font-family: 'Courier New', monospace;
  letter-spacing: 1px;
}

/* 加载进度条样式 */
.loading-progress {
  width: 100%;
}

.progress-bar {
  width: 100%;
  height: 12px;
  background: rgba(0, 0, 0, 0.08);
  border-radius: 8px;
  overflow: hidden;
  margin-bottom: 12px;
  box-shadow:
      inset 0 2px 4px rgba(0, 0, 0, 0.1),
      0 1px 2px rgba(0, 0, 0, 0.05);
  border: 1px solid rgba(0, 0, 0, 0.08);
}

.progress-fill {
  height: 100%;
  background: linear-gradient(135deg, #007bff 0%, #0056b3 50%, #004085 100%);
  border-radius: 8px;
  transition: width 0.4s cubic-bezier(0.4, 0, 0.2, 1);
  position: relative;
  overflow: hidden;
  box-shadow: 0 2px 4px rgba(0, 123, 255, 0.3);
}

.progress-fill::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: linear-gradient(
      90deg,
      transparent 0%,
      rgba(255, 255, 255, 0.4) 30%,
      rgba(255, 255, 255, 0.6) 50%,
      rgba(255, 255, 255, 0.4) 70%,
      transparent 100%
  );
  animation: progressShine 2.5s infinite;
}

.progress-fill::after {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: linear-gradient(
      45deg,
      transparent 30%,
      rgba(255, 255, 255, 0.2) 50%,
      transparent 70%
  );
  animation: progressShimmer 1.5s infinite;
}

@keyframes progressShine {
  0% {
    transform: translateX(-100%);
  }
  100% {
    transform: translateX(100%);
  }
}

@keyframes progressShimmer {
  0%, 100% {
    opacity: 0.3;
  }
  50% {
    opacity: 0.8;
  }
}

.progress-text {
  text-align: center;
  font-size: 16px;
  color: #333;
  font-weight: 700;
  font-family: 'Courier New', monospace;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
  letter-spacing: 1px;
}
</style>
